Trade offs were made but everything fits in EEPROM, preliminary saving is enabled by holding one of the buttons down for 10 seconds.

--- examples/UNTZtrument_Step_Seq/UNTZtrument_Step_Seq.ino
+++ examples/UNTZtrument_Step_Seq/UNTZtrument_Step_Seq.ino
@@ -77,6 +77,8 @@
 unsigned long beatInterval = 60000L / bpm; // ms/beat
 unsigned long prevBeatTime = 0L;           // Column step timer
 unsigned long prevReadTime = 0L;           // Keypad polling timer
+long longButtonPressTime = -1;
+
 uint8_t       playing_grid_set = 0;
 uint8_t       visible_grid_set = 0;
 uint8_t       layer = 0;                   // current layer
@@ -89,21 +91,19 @@
 #define GRID_WIDTH     16
 #define GRID_SETS      8

-uint8_t       grid[GRID_WIDTH][LAYERS][GRID_SETS]; // bitfield
-
-uint8_t       channels[LAYERS][GRID_SETS];  // 1 to 127, 1 bit free
-boolean       long_notes[LAYERS][GRID_SETS] = {
+uint8_t       grid[GRID_WIDTH][LAYERS - 1][GRID_SETS]; // bitfield
+
+uint8_t       channels[LAYERS];  // 1 to 127, 1 bit free
+uint8_t       long_notes[LAYERS] = {
   1, 1, 1, 1, 1, 1, 1, 0}; // 0 to 1, 7 bits free
-uint8_t       noteOffset[LAYERS][GRID_SETS]; // 0 to (MAX_MIDI_NOTES-BUTTON_GRID_WIDTH), 2 bits free
-uint8_t       volume[LAYERS][GRID_SETS]; // 0 to 127, 1 bit free
-uint8_t       instrument[LAYERS][GRID_SETS]; // 1 to 127, 1 bit free
-uint8_t       scale_notes[LAYERS][GRID_SETS]; // 0 to (MAX_SCALES-1), 4 bits free
+uint8_t       noteOffset[LAYERS - 1][GRID_SETS]; // 0 to (MAX_MIDI_NOTES-BUTTON_GRID_WIDTH), 2 bits free
+uint8_t       volume[LAYERS]; // 0 to 127, 1 bit free
+uint8_t       instrument[LAYERS]; // 1 to 127, 1 bit free
+uint8_t       scale_notes[LAYERS]; // 0 to (MAX_SCALES-1), 4 bits free
 // total of 16 bits not used, 128 bytes packed

 uint8_t       grid_width[GRID_SETS];  // 0 to (GRID_WIDTH-1) 4 bits free
 uint8_t       tempo[GRID_SETS]; // todo hook this up to bpm
-// total of 384 + 1024 + 16
-// todo need to get this under 1K to be able ot store everything in EEPROM

 //uint8_t max_grid_width_table[14] = {
 //  8, 9, 10, 12, 14, 15, 16, 20, 21, 24, 25, 27, 28, 30}; // mutiples of 2, 3, 4, 5, 6, 7 that are equal to or more than 8
@@ -120,40 +120,40 @@
 static const uint8_t PROGMEM
 scales[MAX_SCALES][MAX_MIDI_NOTES] = {
   {
-    105, 103, 101, 100, 98, 96, 95, 93, 91, 89, 88, 86, 84, 83, 81, 79, 77, 76, 74, 72, 71, 69, 67, 65, 64, 62, 60, 59, 57, 55, 53, 52, 50, 48, 47, 45, 43, 41, 40, 38, 36, 35, 33, 31, 29, 28, 26, 24, 23, 21, 19, 17, 16, 14          }
+    105, 103, 101, 100, 98, 96, 95, 93, 91, 89, 88, 86, 84, 83, 81, 79, 77, 76, 74, 72, 71, 69, 67, 65, 64, 62, 60, 59, 57, 55, 53, 52, 50, 48, 47, 45, 43, 41, 40, 38, 36, 35, 33, 31, 29, 28, 26, 24, 23, 21, 19, 17, 16, 14                  }
   ,
   {
-    104, 103, 101, 99, 98, 96, 94, 92, 91, 89, 87, 86, 84, 82, 80, 79, 77, 75, 74, 72, 70, 68, 67, 65, 63, 62, 60, 58, 56, 55, 53, 51, 50, 48, 46, 44, 43, 41, 39, 38, 36, 34, 32, 31, 29, 27, 26, 24, 22, 20, 19, 17, 15, 14          }
+    104, 103, 101, 99, 98, 96, 94, 92, 91, 89, 87, 86, 84, 82, 80, 79, 77, 75, 74, 72, 70, 68, 67, 65, 63, 62, 60, 58, 56, 55, 53, 51, 50, 48, 46, 44, 43, 41, 39, 38, 36, 34, 32, 31, 29, 27, 26, 24, 22, 20, 19, 17, 15, 14                  }
   ,
   {
-    86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33          }
+    86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33                  }
   ,
   {
-    113, 111, 108, 106, 103, 102, 101, 99, 96, 94, 91, 90, 89, 87, 84, 82, 79, 78, 77, 75, 72, 70, 67, 66, 65, 63, 60, 58, 55, 54, 53, 51, 48, 46, 43, 42, 41, 39, 36, 34, 31, 30, 29, 27, 24, 22, 19, 18, 17, 15, 12, 10, 7, 6          }
+    113, 111, 108, 106, 103, 102, 101, 99, 96, 94, 91, 90, 89, 87, 84, 82, 79, 78, 77, 75, 72, 70, 67, 66, 65, 63, 60, 58, 55, 54, 53, 51, 48, 46, 43, 42, 41, 39, 36, 34, 31, 30, 29, 27, 24, 22, 19, 18, 17, 15, 12, 10, 7, 6                  }
   ,
   {
-    99, 97, 96, 94, 93, 91, 90, 88, 87, 85, 84, 82, 81, 79, 78, 76, 75, 73, 72, 70, 69, 67, 66, 64, 63, 61, 60, 58, 57, 55, 54, 52, 51, 49, 48, 46, 45, 43, 42, 40, 39, 37, 36, 34, 33, 31, 30, 28, 27, 25, 24, 22, 21, 19          }
+    99, 97, 96, 94, 93, 91, 90, 88, 87, 85, 84, 82, 81, 79, 78, 76, 75, 73, 72, 70, 69, 67, 66, 64, 63, 61, 60, 58, 57, 55, 54, 52, 51, 49, 48, 46, 45, 43, 42, 40, 39, 37, 36, 34, 33, 31, 30, 28, 27, 25, 24, 22, 21, 19                  }
   ,
   {
-    95, 94, 93, 92, 91, 89, 87, 86, 84, 83, 82, 81, 80, 79, 77, 75, 74, 72, 71, 70, 69, 68, 67, 65, 63, 62, 60, 59, 58, 57, 56, 55, 53, 51, 50, 48, 47, 46, 45, 44, 43, 41, 39, 38, 36, 35, 34, 33, 32, 31, 29, 27, 26, 24          }
+    95, 94, 93, 92, 91, 89, 87, 86, 84, 83, 82, 81, 80, 79, 77, 75, 74, 72, 71, 70, 69, 68, 67, 65, 63, 62, 60, 59, 58, 57, 56, 55, 53, 51, 50, 48, 47, 46, 45, 44, 43, 41, 39, 38, 36, 35, 34, 33, 32, 31, 29, 27, 26, 24                  }
   ,
   {
-    104, 103, 101, 100, 98, 96, 95, 92, 91, 89, 88, 86, 84, 83, 80, 79, 77, 76, 74, 72, 71, 68, 67, 65, 64, 62, 60, 59, 56, 55, 53, 52, 50, 48, 47, 44, 43, 41, 40, 38, 36, 35, 32, 31, 29, 28, 26, 24, 23, 20, 19, 17, 16, 14          }
+    104, 103, 101, 100, 98, 96, 95, 92, 91, 89, 88, 86, 84, 83, 80, 79, 77, 76, 74, 72, 71, 68, 67, 65, 64, 62, 60, 59, 56, 55, 53, 52, 50, 48, 47, 44, 43, 41, 40, 38, 36, 35, 32, 31, 29, 28, 26, 24, 23, 20, 19, 17, 16, 14                  }
   ,
   {
-    104, 103, 101, 99, 98, 96, 95, 92, 91, 89, 87, 86, 84, 83, 80, 79, 77, 75, 74, 72, 71, 68, 67, 65, 63, 62, 60, 59, 56, 55, 53, 51, 50, 48, 47, 44, 43, 41, 39, 38, 36, 35, 32, 31, 29, 27, 26, 24, 23, 20, 19, 17, 15, 14          }
+    104, 103, 101, 99, 98, 96, 95, 92, 91, 89, 87, 86, 84, 83, 80, 79, 77, 75, 74, 72, 71, 68, 67, 65, 63, 62, 60, 59, 56, 55, 53, 51, 50, 48, 47, 44, 43, 41, 39, 38, 36, 35, 32, 31, 29, 27, 26, 24, 23, 20, 19, 17, 15, 14                  }
   ,
   {
-    105, 103, 101, 99, 98, 96, 95, 93, 91, 89, 87, 86, 84, 83, 81, 79, 77, 75, 74, 72, 71, 69, 67, 65, 63, 62, 60, 59, 57, 55, 53, 51, 50, 48, 47, 45, 43, 41, 39, 38, 36, 35, 33, 31, 29, 27, 26, 24, 23, 21, 19, 17, 15, 14          }
+    105, 103, 101, 99, 98, 96, 95, 93, 91, 89, 87, 86, 84, 83, 81, 79, 77, 75, 74, 72, 71, 69, 67, 65, 63, 62, 60, 59, 57, 55, 53, 51, 50, 48, 47, 45, 43, 41, 39, 38, 36, 35, 33, 31, 29, 27, 26, 24, 23, 21, 19, 17, 15, 14                  }
   ,
   {
-    111, 110, 108, 107, 105, 103, 99, 98, 96, 95, 93, 91, 87, 86, 84, 83, 81, 79, 75, 74, 72, 71, 69, 67, 63, 62, 60, 59, 57, 55, 51, 50, 48, 47, 45, 43, 39, 38, 36, 35, 33, 31, 27, 26, 24, 23, 21, 19, 15, 14, 12, 11, 9, 7          }
+    111, 110, 108, 107, 105, 103, 99, 98, 96, 95, 93, 91, 87, 86, 84, 83, 81, 79, 75, 74, 72, 71, 69, 67, 63, 62, 60, 59, 57, 55, 51, 50, 48, 47, 45, 43, 39, 38, 36, 35, 33, 31, 27, 26, 24, 23, 21, 19, 15, 14, 12, 11, 9, 7                  }
   ,
   {
-    105, 102, 101, 100, 97, 96, 94, 93, 90, 89, 88, 85, 84, 82, 81, 78, 77, 76, 73, 72, 70, 69, 66, 65, 64, 61, 60, 58, 57, 54, 53, 52, 49, 48, 46, 45, 42, 41, 40, 37, 36, 34, 33, 30, 29, 28, 25, 24, 22, 21, 18, 17, 16, 13          }
+    105, 102, 101, 100, 97, 96, 94, 93, 90, 89, 88, 85, 84, 82, 81, 78, 77, 76, 73, 72, 70, 69, 66, 65, 64, 61, 60, 58, 57, 54, 53, 52, 49, 48, 46, 45, 42, 41, 40, 37, 36, 34, 33, 30, 29, 28, 25, 24, 22, 21, 18, 17, 16, 13                  }
   ,
   {
-    103, 102, 101, 100, 98, 96, 94, 91, 90, 89, 88, 86, 84, 82, 79, 78, 77, 76, 74, 72, 70, 67, 66, 65, 64, 62, 60, 58, 55, 54, 53, 52, 50, 48, 46, 43, 42, 41, 40, 38, 36, 34, 31, 30, 29, 28, 26, 24, 22, 19, 18, 17, 16, 14          }
+    103, 102, 101, 100, 98, 96, 94, 91, 90, 89, 88, 86, 84, 82, 79, 78, 77, 76, 74, 72, 70, 67, 66, 65, 64, 62, 60, 58, 55, 54, 53, 52, 50, 48, 46, 43, 42, 41, 40, 38, 36, 34, 31, 30, 29, 28, 26, 24, 22, 19, 18, 17, 16, 14                  }
 };

 /*
@@ -217,23 +217,62 @@
 void save(void)
 {
   int address = 0;
+  EEPROM.write(address + 0, 0xd5);
+  EEPROM.write(address + 1, 0xaa);
+  EEPROM.write(address + 2, 0x96);
+  EEPROM.write(address + 3, 0x01);
+  address += 4;
   saveToEEPROM(address, (uint8_t *)&grid[0], sizeof(grid));
-  //  address += sizeof(grid);
-  //  saveToEEPROM(address, (uint8_t *)&grid_width[0], sizeof(grid_width));
-  //  address += sizeof(grid_width);
-  //  saveToEEPROM(address, (uint8_t *)&noteOffset[0], sizeof(noteOffset));
-  //  address += sizeof(noteOffset);
+  address += sizeof(grid);
+  saveToEEPROM(address, (uint8_t *)&grid_width[0], sizeof(grid_width));
+  address += sizeof(grid_width);
+  saveToEEPROM(address, (uint8_t *)&noteOffset[0], sizeof(noteOffset));
+  address += sizeof(noteOffset);
+  saveToEEPROM(address, (uint8_t *)&long_notes[0], sizeof(long_notes));
+  address += sizeof(long_notes);
+  saveToEEPROM(address, (uint8_t *)&volume[0], sizeof(volume));
+  address += sizeof(volume);
+  saveToEEPROM(address, (uint8_t *)&instrument[0], sizeof(instrument));
+  address += sizeof(instrument);
+  saveToEEPROM(address, (uint8_t *)&scale_notes[0], sizeof(scale_notes));
+  address += sizeof(scale_notes);
+  saveToEEPROM(address, (uint8_t *)&tempo[0], sizeof(tempo));
+  address += sizeof(tempo);
+  saveToEEPROM(address, (uint8_t *)&channels[0], sizeof(channels));
+  address += sizeof(channels);
+
+  //1012 bytes it fits, barely
 }

 void load(void)
 {
   int address = 0;
-  loadFromEEPROM(address, (uint8_t *)&grid[0], sizeof(grid));
-  //  address += sizeof(grid);
-  //  loadFromEEPROM(address, (uint8_t *)&grid_width[0], sizeof(grid_width));
-  //  address += sizeof(grid_width);
-  //  loadFromEEPROM(address, (uint8_t *)&noteOffset[0], sizeof(noteOffset));
-  //  address += sizeof(noteOffset);
+
+  if ((EEPROM.read(address + 0) == 0xd5) &&
+    (EEPROM.read(address + 1) == 0xaa) &&
+    (EEPROM.read(address + 2) == 0x96) &&
+    (EEPROM.read(address + 3) == 0x01))
+  {
+    address += 4;
+    loadFromEEPROM(address, (uint8_t *)&grid[0], sizeof(grid));
+    address += sizeof(grid);
+    loadFromEEPROM(address, (uint8_t *)&grid_width[0], sizeof(grid_width));
+    address += sizeof(grid_width);
+    loadFromEEPROM(address, (uint8_t *)&noteOffset[0], sizeof(noteOffset));
+    address += sizeof(noteOffset);
+    loadFromEEPROM(address, (uint8_t *)&long_notes[0], sizeof(long_notes));
+    address += sizeof(long_notes);
+    loadFromEEPROM(address, (uint8_t *)&volume[0], sizeof(volume));
+    address += sizeof(volume);
+    loadFromEEPROM(address, (uint8_t *)&instrument[0], sizeof(instrument));
+    address += sizeof(instrument);
+    loadFromEEPROM(address, (uint8_t *)&scale_notes[0], sizeof(scale_notes));
+    address += sizeof(scale_notes);
+    loadFromEEPROM(address, (uint8_t *)&tempo[0], sizeof(tempo));
+    address += sizeof(tempo);
+    loadFromEEPROM(address, (uint8_t *)&channels[0], sizeof(channels));
+    address += sizeof(channels);
+  }
 }

 void setup()
@@ -267,19 +306,16 @@
   memset(scale_notes, 0, sizeof(scale_notes)); // default all grids to major scale
   memset(channels, 2, sizeof(channels)); // default all channels to 2

-  for (uint8_t index = 0; index < GRID_SETS; index++) // default all the last layers to channel 10 for mt32 drum channel
-  {
-    channels[0][index] = 3;
-    channels[1][index] = 4;
-    channels[2][index] = 5;
-    channels[3][index] = 6;
-    channels[4][index] = 7;
-    channels[5][index] = 8;
-    channels[6][index] = 10;
-    channels[7][index] = 2;
-  }
-
-  //  load();
+  channels[0] = 3;
+  channels[1] = 4;
+  channels[2] = 5;
+  channels[3] = 6;
+  channels[4] = 7;
+  channels[5] = 8;
+  channels[6] = 10;
+  channels[7] = 2;
+
+  load();

   eScaleOffset.setBounds(0, 44 * 4 + 3); // Set note offset limits
   eScaleOffset.setValue(noteOffset[layer][visible_grid_set] * 4);              // *4's for encoder detents
@@ -288,7 +324,7 @@
   eDisplayOffset.setBounds(0, (grid_width[visible_grid_set] - BUTTON_GRID_WIDTH) * 4 + 3); // Set diplay offset limits
   eDisplayOffset.setValue(0);              // *4's for encoder detents
   eVolume.setBounds(0, 127 * 4 + 3); // Set volume limits
-  eVolume.setValue(volume[layer][visible_grid_set] * 4);              // *4's for encoder detents
+  eVolume.setValue(volume[layer] * 4);              // *4's for encoder detents
 }

 bool flicker_state = true;
@@ -380,7 +416,7 @@

   if((t - prevReadTime) >= 20L) // 20ms = min Trellis poll time
   {
-
+
     if(untztrument.readSwitches()) // Button state change?
     {
       for(uint8_t i=0; i<N_BUTTONS; i++) // For each button...
@@ -392,8 +428,8 @@
         {
           if (x != 7)
           {
-            uint8_t temp = channels[layer][playing_grid_set];
-            usbMIDI.sendNoteOff(pgm_read_byte(&scales[scale_notes[layer][playing_grid_set]][8-x + y*7] ) , 127, temp);
+            uint8_t temp = channels[layer];
+            usbMIDI.sendNoteOff(pgm_read_byte(&scales[scale_notes[layer]][8-x + y*7] ) , 127, temp);
             untztrument.clrLED(i);

             refresh = true;
@@ -405,22 +441,22 @@
           switch (mode)
           {

-    case PLAYING_ERASE_PROGRAM_CHANGE_1:
-//          case PLAYING_ERASE_PROGRAM_CHANGE_2:
+          case PLAYING_ERASE_PROGRAM_CHANGE_1:
+          case PLAYING_ERASE_PROGRAM_CHANGE_2:
           case PLAYING_ERASE_CHANNEL_1:
-//          case PLAYING_ERASE_CHANNEL_2:
+          case PLAYING_ERASE_CHANNEL_2:
           case PLAYING_ERASE_LAYER_1:
-//          case PLAYING_ERASE_LAYER_2:
+          case PLAYING_ERASE_LAYER_2:
           case PLAYING_ERASE_GRID_1:
-//          case PLAYING_ERASE_GRID_2:
+          case PLAYING_ERASE_GRID_2:
           case PLAYING_ERASE_WIDTH_1:
-//          case PLAYING_ERASE_WIDTH_2:
+          case PLAYING_ERASE_WIDTH_2:
           case PLAYING_ERASE_SCALE_1:
-//          case PLAYING_ERASE_SCALE_2:
+          case PLAYING_ERASE_SCALE_2:
           case PLAYING_ERASE_C_1:
-//          case PLAYING_ERASE_C_2:
+          case PLAYING_ERASE_C_2:
           case PLAYING_ERASE_MEASURE_1:
-//          case PLAYING_ERASE_MEASURE_2:
+          case PLAYING_ERASE_MEASURE_2:
             // fall through
           case PLAYING:
             if (layer != LAYERS-1)
@@ -435,8 +471,8 @@
                 //todo this wont work for other grid set is still playing
                 if (visible_grid_set == playing_grid_set)
                 {
-                  uint8_t temp2 = channels[layer][playing_grid_set];
-                  usbMIDI.sendNoteOff(pgm_read_byte(&scales[scale_notes[layer][playing_grid_set]][y + noteOffset[layer][playing_grid_set]] ) , 127, temp2);
+                  uint8_t temp2 = channels[layer];
+                  usbMIDI.sendNoteOff(pgm_read_byte(&scales[scale_notes[layer]][y + noteOffset[layer][playing_grid_set]] ) , 127, temp2);
                 }
               }
               else // Turn on
@@ -449,8 +485,8 @@
             {
               if (x != 7)
               {
-                uint8_t temp = channels[layer][playing_grid_set];
-                usbMIDI.sendNoteOn(pgm_read_byte(&scales[scale_notes[layer][playing_grid_set]][8-x + y*7] ) , 127, temp);
+                uint8_t temp = channels[layer];
+                usbMIDI.sendNoteOn(pgm_read_byte(&scales[scale_notes[layer]][8-x + y*7] ) , 127, temp);
                 untztrument.setLED(i);
               }
               else
@@ -464,7 +500,7 @@

           case WAIT_FOR_PROGRAM_CHANGE:
             {
-              uint8_t temp = instrument[layer][visible_grid_set];
+              uint8_t temp = instrument[layer];

               if (temp >= 64)
               {
@@ -476,9 +512,9 @@
               line_horz(temp / 8, false);

               temp = x+y*8;
-              instrument[layer][visible_grid_set] = temp;
-
-              usbMIDI.sendProgramChange(temp, channels[layer][playing_grid_set]);
+              instrument[layer] = temp;
+
+              usbMIDI.sendProgramChange(temp, channels[layer]);
               force_first_note = true;

               // Turn on column and row for a beat to indicate selection
@@ -492,7 +528,7 @@

           case WAIT_FOR_PROGRAM_CHANGE_2:
             {
-              uint8_t temp = instrument[layer][visible_grid_set];
+              uint8_t temp = instrument[layer];
               if (temp >= 64)
               {
                 temp = temp - 64;
@@ -503,9 +539,9 @@
               line_horz(temp / 8, false);

               temp = x + y*8 + 64;
-              instrument[layer][visible_grid_set] = temp;
-
-              usbMIDI.sendProgramChange(temp, channels[layer][playing_grid_set]);
+              instrument[layer] = temp;
+
+              usbMIDI.sendProgramChange(temp, channels[layer]);
               force_first_note = true;

               // Turn on column and row for a beat to indicate selection
@@ -536,9 +572,7 @@

               // adjust encoder ranges to match current layer
               eScaleOffset.setValue(noteOffset[layer][visible_grid_set] * 4);              // *4's for encoder detents
-              eVolume.setValue(volume[layer][visible_grid_set] * 4);              // *4's for encoder detents
-
-              //save(); // save on layer change, todo too slow, need to do it a byte at a time or at some point that won't hurt performance
+              eVolume.setValue(volume[layer] * 4);              // *4's for encoder detents

               refresh = true;
               break;
@@ -606,7 +640,7 @@

           case WAIT_FOR_SCALE:
             {
-              uint8_t temp = scale_notes[layer][visible_grid_set];
+              uint8_t temp = scale_notes[layer];

               if (temp > MAX_SCALES)
               {
@@ -622,7 +656,7 @@
               {
                 temp = MAX_SCALES - 1;
               }
-              scale_notes[layer][visible_grid_set] = temp;
+              scale_notes[layer] = temp;

               force_first_note = true;

@@ -637,14 +671,14 @@

           case WAIT_FOR_CHANNEL:
             {
-              uint8_t temp = channels[layer][visible_grid_set];
+              uint8_t temp = channels[layer];

               // Turn off column and row
               line_vert(temp % 8, false);
               line_horz(temp / 8, false);

               temp = x+y*8;
-              channels[layer][visible_grid_set] = temp;
+              channels[layer] = temp;

               force_first_note = true;

@@ -746,8 +780,8 @@
       {
         if((grid[col][layer_index][playing_grid_set] & mask) && (!(grid[(col + 1) % grid_width[playing_grid_set]][layer_index][playing_grid_set] & mask) || ((col == (grid_width[playing_grid_set] - 1)) && (playing_grid_set != visible_grid_set))))
         {
-          uint8_t temp = channels[layer_index][playing_grid_set];
-          usbMIDI.sendNoteOff(pgm_read_byte(&scales[scale_notes[layer_index][playing_grid_set]][row + noteOffset[layer_index][playing_grid_set]] ) , 127, temp);
+          uint8_t temp = channels[layer_index];
+          usbMIDI.sendNoteOff(pgm_read_byte(&scales[scale_notes[layer_index]][row + noteOffset[layer_index][playing_grid_set]] ) , 127, temp);
         }
       }
     }
@@ -766,12 +800,12 @@
     // adjust the volume here so all notes are off when we make the change
     // todo add display of currnet volume level with a vertical bar, possibley show other layer volumes
     temp = eVolume.getValue() / 4;
-    diff = volume[layer][visible_grid_set] - temp;
+    diff = volume[layer] - temp;
     if (diff != 0)
     {
-      volume[layer][visible_grid_set] = temp;
+      volume[layer] = temp;
       // send change volume on channels
-      usbMIDI.sendControlChange(7, temp, channels[layer][playing_grid_set]);
+      usbMIDI.sendControlChange(7, temp, channels[layer]);
     }

     // Turn on new column
@@ -784,8 +818,8 @@
       {
         if((grid[col][layer_index][playing_grid_set] & mask) && ((!(grid[(col + grid_width[playing_grid_set] - 1) % grid_width[playing_grid_set]][layer_index][playing_grid_set] & mask)) || (force_first_note == true)))
         {
-          uint8_t temp = channels[layer_index][playing_grid_set];
-          usbMIDI.sendNoteOn(pgm_read_byte(&scales[scale_notes[layer_index][playing_grid_set]][row + noteOffset[layer_index][playing_grid_set]]), 127, temp);
+          uint8_t temp = channels[layer_index];
+          usbMIDI.sendNoteOn(pgm_read_byte(&scales[scale_notes[layer_index]][row + noteOffset[layer_index][playing_grid_set]]), 127, temp);
           force_first_note = false; // clear trigger if it was set
         }
       }
@@ -812,7 +846,7 @@
   case WAIT_FOR_PROGRAM_CHANGE_2:
     {
       // Turn on column and row for a beat to indicate current selection
-      uint8_t temp = instrument[layer][visible_grid_set];
+      uint8_t temp = instrument[layer];

       if (temp >= 64)
       {
@@ -828,7 +862,7 @@
   case WAIT_FOR_SCALE:
     {
       // Turn on column and row for a beat to indicate current selection
-      uint8_t temp = scale_notes[layer][visible_grid_set];
+      uint8_t temp = scale_notes[layer];

       if (temp > MAX_SCALES)
       {
@@ -845,7 +879,7 @@
   case WAIT_FOR_CHANNEL:
     {
       // Turn on column and row for a beat to indicate current selection
-      uint8_t temp = channels[layer][visible_grid_set];
+      uint8_t temp = channels[layer];
       line_vert(temp % 8, true);
       line_horz(temp / 8, true);
       break;
@@ -893,7 +927,7 @@

   case PLAYING_ERASE_PROGRAM_CHANGE_2:
     {
-      uint8_t temp = instrument[layer][visible_grid_set];
+      uint8_t temp = instrument[layer];

       if (temp >= 64)
       {
@@ -908,7 +942,7 @@

   case PLAYING_ERASE_SCALE_2:
     {
-      uint8_t temp = scale_notes[layer][visible_grid_set];
+      uint8_t temp = scale_notes[layer];

       if (temp > MAX_SCALES)
       {
@@ -923,7 +957,7 @@

   case PLAYING_ERASE_CHANNEL_2:
     {
-      uint8_t temp = channels[layer][visible_grid_set];
+      uint8_t temp = channels[layer];

       // Turn off column and row after selection
       line_vert(temp % 8, false);
@@ -981,7 +1015,7 @@
   default:
     break;
   }
-
+
   enc::poll(); // Read encoder(s)

   // todo temp disable until we have a button to hook it to
@@ -1009,7 +1043,7 @@
       mode = WAIT_FOR_SCALE;

       // Turn on row for a beat to indicate current selection
-      uint8_t temp = scale_notes[layer][visible_grid_set];
+      uint8_t temp = scale_notes[layer];

       if (temp > MAX_SCALES)
       {
@@ -1023,7 +1057,7 @@
     else if ((sensor0Val == LOW) && (mode == WAIT_FOR_SCALE)) // pressed the button twice
     {
       // Turn off column and row for a beat to indicate current selection
-      uint8_t temp = scale_notes[layer][visible_grid_set];
+      uint8_t temp = scale_notes[layer];

       if (temp > MAX_SCALES)
       {
@@ -1034,7 +1068,7 @@
       line_horz(temp / 8, false);

       // Turn off column and row for a beat to indicate current selection
-      temp = channels[layer][visible_grid_set];
+      temp = channels[layer];

       line_vert(temp % 8, true);
       line_horz(temp / 8, true);
@@ -1045,7 +1079,7 @@
     else if ((sensor0Val == LOW) && (mode == WAIT_FOR_CHANNEL)) // pressed the button twice
     {
       // Turn off column and row for a beat to indicate current selection
-      uint8_t temp = channels[layer][visible_grid_set];
+      uint8_t temp = channels[layer];

       line_vert(temp % 8, false);
       line_horz(temp / 8, false);
@@ -1060,6 +1094,7 @@
   sensor13Val = digitalRead(BUTTON_WIDTH);
   if (sensor13Val != lastSensor13Val)
   {
+    longButtonPressTime = t;
     if ((sensor13Val == LOW) && (mode == PLAYING))
     {
       mode = WAIT_FOR_WIDTH;
@@ -1083,6 +1118,12 @@
       mode = PLAYING;
     }
   }
+  else if ((t - longButtonPressTime > 10000) && (sensor13Val == LOW))
+  {
+    longButtonPressTime = -1;
+    //line_horz(1, true);
+    save();
+  }
   lastSensor13Val = sensor13Val;

@@ -1095,7 +1136,7 @@
       mode = WAIT_FOR_PROGRAM_CHANGE;

       // Turn on column and row for a beat to indicate current selection
-      uint8_t temp = instrument[layer][visible_grid_set];
+      uint8_t temp = instrument[layer];

       if (temp >= 64)
       {
@@ -1113,7 +1154,7 @@
     else if ((sensor1Val == LOW) && (mode == WAIT_FOR_PROGRAM_CHANGE_2)) // pressed the button twice
     {
       // Turn off column and row for a beat to indicate current selection
-      uint8_t temp = instrument[layer][visible_grid_set];
+      uint8_t temp = instrument[layer];

       if (temp >= 64)
       {
@@ -1244,3 +1285,7 @@

+
+
+
+


Change unit tests to use ArduinoUnit FakeStream

--- test/unit/firmata_test/firmata_test.ino
+++ test/unit/firmata_test/firmata_test.ino
@@ -13,54 +13,6 @@
   suite.run();
 }

-class InMemoryStream : public Stream
-{
-public:
-  virtual ~InMemoryStream()
-  {
-  }
-
-  size_t write(uint8_t val)
-  {
-    _bytesWritten += (char) val;
-
-    return size_t(1);
-  }
-
-  void flush()
-  {
-  }
-
-  const String& bytesWritten()
-  {
-    return _bytesWritten;
-  }
-
-  void nextByte(byte b)
-  {
-    _nextByte = b;
-  }
-
-  int available()
-  {
-    return 1;
-  }
-
-  int read()
-  {
-    return _nextByte;
-  }
-
-  int peek()
-  {
-    return _nextByte;
-  }
-
-private:
-  String _bytesWritten;
-  byte _nextByte;
-};
-
 void assertStringsEqual(Test& __test__, const char* expected, const String& actual)
 {
   size_t expectedLength = strlen(expected);
@@ -73,15 +25,15 @@

 test(beginPrintsVersion)
 {
-  InMemoryStream stream;
+  FakeStream stream;

   Firmata.begin(stream);

   char expected[] =
   {
-    0xF9, // Version reporting identifier
-    2,    // Major version number
-    3,    // Minor version number
+    REPORT_VERSION,
+    FIRMATA_MAJOR_VERSION,
+    FIRMATA_MINOR_VERSION,
     0
   };
   assertStringsEqual(__test__, expected, stream.bytesWritten());
@@ -89,7 +41,7 @@

 void processMessage(const byte* message, size_t length)
 {
-  InMemoryStream stream;
+  FakeStream stream;
   Firmata.begin(stream);

   for (size_t i = 0; i < length; i++)
@@ -178,3 +130,12 @@
   assertEquals(1, _digitalPort);
 }

+test(setFirmwareVersionDoesNotLeakMemory)
+{
+  Firmata.setFirmwareVersion(1, 0);
+  int initialMemory = freeMemory();
+
+  Firmata.setFirmwareVersion(1, 0);
+
+  assertEquals(0, initialMemory - freeMemory());
+}


Added a Menu Frame (for further development).

--- code/oakpowerboard/oakpowerboard.ino
+++ code/oakpowerboard/oakpowerboard.ino
@@ -54,6 +54,7 @@
 /* Drawing Resources */
 bool draw_totalcount_frame(SSD1306 *display, SSD1306UiState* state, int x, int y);
 bool draw_currentconsumption_frame(SSD1306 *display, SSD1306UiState* state, int x, int y);
+bool draw_menu_frame(SSD1306 *display, SSD1306UiState* state, int x, int y);
 bool msOverlay(SSD1306 *display, SSD1306UiState* state);

 float get_consumption_from_time_since_last_pulse(long timeSinceLastPulse);
@@ -64,6 +65,15 @@
   draw_totalcount_frame,
   draw_currentconsumption_frame
 };
+
+// this array keeps function pointers to all frames
+// frames are the single views that slide from right to left
+bool (*others[])(SSD1306 *display, SSD1306UiState* state, int x, int y) = {
+  draw_menu_frame,
+  draw_currentconsumption_frame
+};
+
+

 bool (*overlays[])(SSD1306 *display, SSD1306UiState* state) = {
   msOverlay
@@ -128,25 +138,31 @@
   if (remainingTimeBudget > 0) {
     yield();
     if(b1_pressed == true) {
+      //handle b1
+      ui.setFrames(others, 2);
+      ui.disableAutoTransition();
+      b1_pressed = false;
+      yield();
+    }
+
+    if(b2_pressed == true) {
+      //handle b2
       ui.nextFrame();
-      b1_pressed = false;
-      yield();
-    }
-
-    if(b2_pressed == true) {
-      ui.previousFrame();
       b2_pressed = false;
       yield();
     }

     if(b3_pressed == true) {
       //handle b3
+      ui.previousFrame();
       b3_pressed = false;
       yield();
     }

     if(b4_pressed = true) {
       //handle b4
+      ui.setFrames(frames, 2);
+      ui.enableAutoTransition();
       b4_pressed = false;
       yield();
     }
@@ -159,6 +175,7 @@
     }

     if(pulse_triggered == true) {
+    /*if(pulse_triggered == true) {
         storage.pulse_count_current_kwh++;
         if(storage.pulse_count_current_kwh == PULSES_PER_KWH) {
           storage.consumption_total++;
@@ -200,6 +217,13 @@
   return false;
 }

+bool draw_menu_frame(SSD1306 *display, SSD1306UiState* state, int x, int y) {
+  display->setTextAlignment(TEXT_ALIGN_LEFT);
+  display->setFont(Roboto_Plain_10);
+  display->drawString(0 + x, 20, "MENU: (W)");
+  return false;
+}
+
 bool msOverlay(SSD1306 *display, SSD1306UiState* state) {
   //display->drawXbm(32, 0, 8, 8, wifiActive);
   display->setTextAlignment(TEXT_ALIGN_LEFT);


Update pin definition because of design bodge.

--- code/oakpowerboard/oakpowerboard.ino
+++ code/oakpowerboard/oakpowerboard.ino
@@ -20,7 +20,7 @@
 #define BTN_2 8
 #define BTN_3 7
 #define BTN_4 6
-#define PULSE_PIN 5
+#define PULSE_PIN A0
 #define DEBUG_LED 10

 #define EEPROM_READ_ADDR 0


Delete OTTO_ServoHome.ino

Delete OTTO_ServoHome.ino


refactoring

refactoring

--- ESP8266-code/Log-PIR-Switch.ino
+++ ESP8266-code/Log-PIR-Switch.ino
@@ -6,39 +6,52 @@
 const char* streamId   = "stream";
 const char* privateKey = "pk";

-//switch is debounced
-const int switch_pin = 4;
-volatile boolean switch_isClosed = true;
-volatile boolean switch_changed = false;
-volatile unsigned long startMillis = 0;
+// sms
+char* myphone ="447421119309";
+char* sms_pin = "1564";
+
+
+// door switch is debounced
+const int door_pin = 14;
+volatile boolean door_isClosed;
+volatile boolean door_changed = false;
+volatile unsigned long start_ms;
+unsigned int door_ms;
 const long debounce_interval = 3 * 1000;  //3 seconds is OK for a door opening
+boolean warning_sent = false;
+const long door_wait = 30 * 1000;  // longer than 30 seconds in practice

 //pir  transition to no movement only after a period of inactivity
-const int pir_pin = 5;
-volatile boolean pir_on = false;
+const int pir_pin = 15;
+volatile boolean pir_active ;
 volatile boolean pir_changed = false;
-unsigned long pir_millis = 0;
+unsigned long pir_ms;
 boolean pir_movement = false;
-
+unsigned long movement_ms;
 const long inactive_interval = 60 * 1000;  // wait 60 seconds after pir false before signaling no activity

-void switch_change() {
+int seconds(long ms) {
+   return round(ms/1000);
+}
+
+void door_change() {
 // ignore changes within debounce_interval
     unsigned long now = millis();
-    if (now - startMillis > debounce_interval) {
-        startMillis = now;
-        switch_isClosed = digitalRead(switch_pin);
-        switch_changed = true;
+    if (now - start_ms > debounce_interval) {
+        start_ms = now;
+        door_isClosed = digitalRead(door_pin);
+        door_changed = true;
     }
 }

 void pir_change() {
-   pir_on= digitalRead(pir_pin);
+   pir_active= digitalRead(pir_pin);
    pir_changed= true;
 }

 void httpget(String url) {
    WiFiClient client;
+   url.replace(" ", "+");
    int httpPort = 80;
    while (true) {
     if (client.connect(host, httpPort)) break;
@@ -67,29 +80,31 @@
   Serial.println("closing connection");
 }

-void rtlog (char* field, boolean state) {
+void log_data(String params) {
    String url = "/rt/home.xq?_action=store&_id=";
    url += streamId;
-   url += "&_pk=";
-   url += privateKey;
-   url += String("&")+field+"=";
-   url += state;
-   httpget(url);
+   url += String("&_pk=")+ privateKey + "&";
+   url += params;
+   httpget(url);
 }

+void send_sms(String text,char* destination) {
+  String url = String("/sms/xquery/sendsms.xq?pin=")+sms_pin+"&text="+text+"&destination="+destination;
+  httpget(url);
+}
 void setup () {
   Serial.begin(9600);
   delay(10);

  // set switch pin to be interrupt
-  pinMode(switch_pin,OUTPUT);
-  attachInterrupt(digitalPinToInterrupt(switch_pin),switch_change,CHANGE);
-  Serial.println("using Reed switch in pin 4 (d1)");
+  pinMode(door_pin,OUTPUT);
+  attachInterrupt(digitalPinToInterrupt(door_pin),door_change,CHANGE);
+  Serial.println(String("Door switch on pin ") + door_pin);

  // set PIR pin to be interrupt
   pinMode(pir_pin,INPUT);
   attachInterrupt(digitalPinToInterrupt(pir_pin),pir_change,CHANGE);
-  Serial.println("using PIR in pin 5 (d2)");
+  Serial.println(String("PIR on pin ") + pir_pin);

 // Connect to WiFi network
   Serial.println();
@@ -111,37 +126,57 @@
   Serial.print("http://");
   Serial.print(WiFi.localIP());
   Serial.println("/");
+
+  // initialize states and time marks
+  unsigned long now = millis();
+  pir_ms=now;
+  pir_active = digitalRead(pir_pin);
+  door_ms=now;
+  door_isClosed = digitalRead(door_pin);
+  movement_ms=now;
+  start_ms=now;
+}

-}
 void loop() {
-    unsigned long now = millis();
-    if (switch_changed) {
+    long now = millis();
+
+    if (door_changed) {
       Serial.print(now/1000);
-      Serial.print(" Switch ");
-      Serial.print(switch_isClosed);
-      if (switch_isClosed)
-            Serial.println(" door closed");
-      else
-            Serial.println(" door open");
-            rtlog("door",switch_isClosed);
-      switch_changed=false;
+      Serial.print(" Door ");
+      Serial.print(door_isClosed);
+      log_data(String("door=") + door_isClosed + "&interval=" + seconds(now - door_ms) );
+      door_ms= now;
+      door_changed=false;
+      warning_sent=false;
     }
-    if (pir_changed){
+
+// if door has been left open for longer than door_wait then send SMS
+
+    if ( ! door_isClosed  && now - door_ms > door_wait && ! warning_sent) {
+          send_sms(String("door has been open for ")+seconds(now - door_ms)+ " seconds. Please shut it.",myphone);
+          Serial.println(" SMS warning sent");
+          warning_sent=true;
+    }
+
+    if (pir_changed) {
       Serial.print(now/1000);
       Serial.print(" PIR ");
-      Serial.println(pir_on);
-      pir_millis = now;
-      if (pir_on && ! pir_movement) {
+      Serial.println(pir_active);
+      if (pir_active && ! pir_movement) {
               pir_movement = true;
               Serial.println(" movement");
-              rtlog("movement",pir_movement);
-          }
+              log_data(String("empty=") + seconds(now - movement_ms));
+              movement_ms = now;
+           }
       pir_changed = false;
+      pir_ms = now;
     }
-    if ( ! pir_on && pir_movement && now - pir_millis > inactive_interval) {
+
+    if ( ! pir_active && pir_movement && now - pir_ms > inactive_interval) {
           pir_movement=false;
           Serial.println(" no movement");
-          rtlog("movement",pir_movement);
+          log_data(String("occupied=") + seconds(now - movement_ms));;
+          movement_ms = now;
     }

 //


Mandelbrot set

Mandelbrot set

--- FancyEPD_Demo/FancyEPD_Demo.ino
+++ FancyEPD_Demo/FancyEPD_Demo.ino
@@ -110,6 +110,8 @@
 }

 void test_image_layers() {
+  epd.setRotation(1);
+
   // Framebuffer
   int16_t w = epd.width();
   int16_t h = epd.height();
@@ -118,12 +120,49 @@
   int16_t rx = random(w);
   int16_t ry = random(h);

-  // Draw stuff
+  // Nice XOR pattern
+  /*
+  uint8_t mask = 0x1;
+  uint8_t bits = 8;
   for (int16_t x = 0; x < w; x++) {
     for (int16_t y = 0; y < h; y++) {
       buf[y * w + x] = (x + rx) ^ (y + ry);
     }
   }
+  */
+
+  // Mandelbrot
+  uint8_t mask = 0x10;
+  uint8_t bits = 4;
+
+  float xMult = 4.0 / w;
+  float yMult = xMult;
+
+  for (int16_t xi = 0; xi < w; xi++) {
+    for (int16_t yi = 0; yi < h; yi++) {
+      float xf = (xi - (w >> 1)) * xMult;
+      float yf = (yi - (h >> 1)) * yMult;
+
+      float real = xf;
+      float imag = yf;
+
+      // Mandelbrot. Iterations: z' = z*z + c
+      uint8_t i = 0;
+      for (; i < 16; i++) {
+        float re = (real * real) - (imag * imag) + xf;
+        float im = 2.0 * real * imag + yf;
+
+        if ((re < -2.0) || (2.0 < re)) {
+          break;
+        }
+
+        real = re;
+        imag = im;
+      }
+
+      buf[yi * w + xi] = ((15 - i) << 4);
+    }
+  }

   epd.clearBuffer();
   epd.update(k_update_quick_refresh);
@@ -131,9 +170,7 @@
   epd.setCustomTiming(k_update_no_blink, 6);

   // Send each image layer
-  for (uint8_t i = 0; i < 8; i++) {
-    uint8_t mask = (0x01 << i);
-
+  for (uint8_t i = 0; i < bits; i++) {
     for (int16_t x = 0; x < w; x++) {
       for (int16_t y = 0; y < h; y++) {
         epd.drawPixel(x, y, ((buf[y * w + x] & mask) ? 1 : 0));
@@ -141,6 +178,8 @@
     }

     epd.update(k_update_no_blink);
+
+    mask <<= 1;
   }

   delay(DELAY_BETWEEN_IMAGES_MS);


Added more code to send SSID, change, and get SSID from the local file.

--- BackEnd/BackEnd.ino
+++ BackEnd/BackEnd.ino
@@ -16,7 +16,7 @@
 */

 // If debbuging declare
-#define DEBUG_CAT
+//#define DEBUG_CAT

 #include <Dhcp.h>
 #include <Dns.h>
@@ -162,9 +162,9 @@
 // Configuration file
 #define CONFIG_FILE_MAX_SIZE WEB_SOCKET_BUFFER_SIZE
 #define CONFIG_FILE_FULL_PATH "/home/root/hardware.conf"
-#define HOSTAPD_CONFIG_FILE_FULL_PATH "/etc/hostapd/hostapd_test.conf"
+#define HOSTAPD_CONFIG_FILE_FULL_PATH "/etc/hostapd/hostapd.conf"
 //#define TEMP_CONFIG_FILE_FULL_PATH "/etc/hostapd/hostapd_tmp.conf"
-#define TEMP_CONFIG_FILE_FULL_PATH "/etc/hostapd/hostapdTest.conf"
+#define TEMP_CONFIG_FILE_FULL_PATH "/etc/hostapd/hostapdTemp.conf"

 // Scripts
 #define START_ACCESS_POINT_SCRIPT_FULL_PATH "/home/root/startAP"
@@ -207,16 +207,13 @@

 static const struct serveable whitelist[] = {
   {
-    "/favicon.ico", "image/x-icon"                                           }
+    "/favicon.png", "image/png"                                           }
   ,
   {
     "/static/css/app.css", "text/css"                                           }
   ,
   {
     "/static/css/bootstrap.min.css", "text/css"                                           }
-  ,
-  {
-    "/static/css/play_mode.css", "text/css"                                           }
   ,
   {
     "/static/fonts/droid-sans/DroidSans.ttf", "application/x-font-ttf"                                           }
@@ -276,6 +273,12 @@
     "/templates/connect.html", "text/html"                                           }
   ,
   {
+    "/templates/add_remove_pins.html", "text/html"                                           }
+  ,
+  {
+    "/templates/app_settings.html", "text/html"                                           }
+  ,
+  {
     "/templates/pin.html", "text/html"                                           }
   ,
   {
@@ -285,11 +288,29 @@
     "/templates/pin_settings.html", "text/html"                                           }
   ,
   {
+    "/templates/pin_slider.html", "text/html"                                           }
+  ,
+  {
+    "/templates/pin_settings_directive.html", "text/html"                                           }
+  ,
+  {
     "/templates/pin_stub.html", "text/html"                                           }
   ,
   {
     "/templates/play.html", "text/html"                                           }
   ,
+  {
+    "/templates/remove_pin_dialog.html", "text/html"                                           }
+  ,
+  {
+    "/templates/reset_dialog.html", "text/html"                                           }
+  ,
+  {
+    "/templates/ssid_changed.html", "text/html"                                           }
+  ,
+  {
+    "/templates/ssid_dialog.html", "text/html"                                           }
+  ,
   // last one is the default served if no match
   {
     "/index.html", "text/html"                                           }
@@ -688,6 +709,9 @@

   // Clear SSID string
   memset(g_sSsid, '\0', SSID_MAX_LENGTH);
+  //sprintf(g_sSsid,"%s","CAT2");
+  getSsidName(g_sSsid);
+

   // Initialize all pins with no lable and 0.0 value
   for(int i=0; i<TOTAL_NUM_OF_PINS; i++)
@@ -902,6 +926,9 @@
   // Creating status JSON object
   aJsonObject* poStatus = aJson.createObject();

+  // Create SSID object
+  aJsonObject* poSsid = aJson.createObject();
+
   // Creating pin JSON objects
   aJsonObject* poPins = aJson.createObject();
   aJsonObject* apoPin[TOTAL_NUM_OF_PINS];
@@ -918,6 +945,9 @@

   // Create STATUS
   poStatus = aJson.createItem("OK");
+
+  // Create SSID
+  poSsid = aJson.createItem(g_sSsid);

   // Create PINS
   int iaPinConnects[TOTAL_NUM_OF_PINS];
@@ -1030,12 +1060,12 @@

   // Push to JSON object
   aJson.addItemToObject(poJsonBoardState,"status",poStatus);
+  aJson.addItemToObject(poJsonBoardState,"ssid",poSsid);
   aJson.addItemToObject(poJsonBoardState,"pins",poPins);
 //  aJson.addItemToObject(poJsonBoardState,"connections",poConnections);
   aJson.addItemToObject(poJsonBoardState,"connections",paConnections);
   aJson.addItemToObject(poJsonBoardState,"message_ids_processed",paMsgIds);

-
   return poJsonBoardState;

 }
@@ -1119,9 +1149,14 @@
 void procSsidMsg( aJsonObject *_pJsonSsid )
 {
   aJsonObject *poSsid = aJson.getObjectItem(_pJsonSsid, "ssid");
-  if (poSsid && String(poSsid->valuestring).length() <= SSID_MAX_LENGTH)
-    sprintf(g_sSsid, "%s", poSsid->valuestring);
-
+  if ( poSsid && String(poSsid->valuestring).length() <= SSID_MAX_LENGTH )
+  {
+    if( strcmp(g_sSsid, poSsid->valuestring) )
+    {
+      sprintf(g_sSsid, "%s", poSsid->valuestring);
+      setSsidName(poSsid->valuestring);
+    }
+  }
  }

 ///////////////////////////////
@@ -1331,8 +1366,47 @@

 }

-
-int changeSsidName(char *_sSsidName)
+int getSsidName(char *_sSsidName)
+{
+  // We need a buffer to read in data
+  char      Buffer[LOCAL_BUFFER_SIZE];
+
+  // Open the file for reading/write.
+  FILE     *Input = fopen(HOSTAPD_CONFIG_FILE_FULL_PATH, "r"); // Read/write
+
+  // Our find and replace arguments
+  char     *Find = "ssid=";
+
+  if(NULL == Input)
+  {
+      trace_info("%s(): %s file not found", __func__,HOSTAPD_CONFIG_FILE_FULL_PATH);
+      return 1;
+  }
+
+  // For each line...
+  while(NULL != fgets(Buffer, LOCAL_BUFFER_SIZE, Input))
+  {
+    char *SubStr = NULL;    // Where 'ssid=' was found in the line
+    char *Line = Buffer; // Start at the beginning of the line, and after each match
+
+    // Find match
+    SubStr = strstr(Line, Find);
+    if( SubStr )
+    {
+      int iSsidLen = strlen(Line)-strlen(Find);
+      // Truncate Ssid if it too long
+      int n = iSsidLen < SSID_MAX_LENGTH ? iSsidLen : SSID_MAX_LENGTH;
+      strncpy(_sSsidName, SubStr+strlen(Find), n);
+    }
+  }
+
+  // Close our files
+  fclose(Input);
+
+  return 0;
+}
+
+int setSsidName(char *_sSsidName)
 {
   // Truncate Ssid if it too long
   char sSsidName[LOCAL_BUFFER_SIZE];
@@ -1341,7 +1415,7 @@

 // We need a buffer to read in data
   char      Buffer[LOCAL_BUFFER_SIZE];
-  char      TempBuffer[LOCAL_BUFFER_SIZE];
+//  char      TempBuffer[LOCAL_BUFFER_SIZE];

   // Open the file for reading/write.
   FILE     *Input = fopen(HOSTAPD_CONFIG_FILE_FULL_PATH, "r"); // Read/write
@@ -1363,7 +1437,7 @@
   // For each line...
   while(NULL != fgets(Buffer, LOCAL_BUFFER_SIZE, Input))
   {
-    char *Start = NULL;    // Where 'ssid=' was found in the line
+ //   char *Start = NULL;    // Where 'ssid=' was found in the line
     char *Line = Buffer; // Start at the beginning of the line, and after each match

     // Find match
@@ -1431,7 +1505,7 @@
     case DOWN:

         Serial.println("Changing SSID");
-        changeSsidName("CAT1");
+        setSsidName("CAT1");

 //        system("shutdown -r now");


Variable cleanup

Variable cleanup

--- Firmware/R2-Quadcopter/R2-Quadcopter.ino
+++ Firmware/R2-Quadcopter/R2-Quadcopter.ino
@@ -25,14 +25,8 @@
 // RX
 int throttle = THROTTLE_RMIN;
 volatile int input[4];
-volatile unsigned long chrono_start0;
-volatile unsigned long chrono_start1;
-volatile unsigned long chrono_start2;
-volatile unsigned long chrono_start3;
-volatile int last_interrupt_time0;
-volatile int last_interrupt_time1;
-volatile int last_interrupt_time2;
-volatile int last_interrupt_time3;
+volatile unsigned long chrono_start0, chrono_start1, chrono_start2, chrono_start3;
+volatile int last_interrupt_time0, last_interrupt_time1, last_interrupt_time2, last_interrupt_time3;

 // IMU
 float roll_speed, roll_angle;


Delete Distance_Sensor.ino

Delete Distance_Sensor.ino

--- Distance_Sensor.ino
+++ Distance_Sensor.ino
@@ -1,93 +0,0 @@
-//Distance Sensor
-//Feature is height
-//Pre-known object of different heights
-//Pattern is see what the object is closest to.
-
-#include <NewPing.h>
-
-#define TRIGGER_PIN 12
-#define ECHO_PIN 11
-#define MAX_DISTANCE 50
-#define K_NEAREST_NEIGHBORS 1
-
-NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
-
-struct Features {
-  int height;
-};
-
-Features Feature_Extraction() {
-  Features features;
-  //Get height
-
-  int dist = sonar.ping_cm();
-
-  features.height = MAX_DISTANCE - dist;
-
-  return features;
-}
-
-String Pattern_Recognition(Features features) {
-  //Banana is 3-7cm (3 <= x < 7)
-  //Apple-Orange height is 7-11cm (7 <= x < 11)
-  //Small box is 11-25cm (11 <= x < 25)
-  //Medium box is 25-35cm (25 <= x < 35)
-  //Big box is 35-45cm (35 <= x < 45)
-  //Pre-known object of different heights
-  //Pattern is see what the object is closest to.
-
-  String closest_object;
-
-  Serial.print("Object height: ");
-  Serial.println(features.height);
-
-  //k nearest neighbors
-
-  if(features.height >= 3 && features.height < 7) {
-    closest_object = "Banana";
-  }
-  else if(features.height >= 7 && features.height < 11) {
-    closest_object = "Apple or Orange";
-  }
-  else if(features.height >= 11 && features.height < 25) {
-    closest_object = "Small box";
-  }
-  else if(features.height >= 25 && features.height < 35) {
-    closest_object = "Medium box";
-  }
-  else if(features.height >= 35 && features.height < 45) {
-    closest_object = "Large box";
-  }
-  else {
-    closest_object = "";
-  }
-  return closest_object;
-}
-
-void Actuation(String object) {
-  if(object != "") {
-    Serial.print("\t");
-    Serial.println(object);
-  }
-}
-
-void setup() {
-
-  Serial.begin(9600);
-}
-
-void loop() {
-  //Turns both LEDs off before each reading
-
-  //Should put feature extraction and pattern recognition in one multi-second for loop to
-    //give the box time to go through the sensors.
-  Features feature_states;
-  feature_states = Feature_Extraction();
-
-  String closest_object;
-  closest_object = Pattern_Recognition(feature_states);
-
-  Actuation(closest_object);
-
-  delay(1000);
-}


Update Example.ino

Update Example.ino

--- Example/Example.ino
+++ Example/Example.ino
@@ -1,125 +1,128 @@
-#include <SPI.h>
-#include <WiFi101.h>
-#include "Adafruit_MQTT.h"
-#include "Adafruit_MQTT_Client.h"
-
-char ssid[] = "84885029";     //  your network SSID (name)
-char pass[] = "1030634853o0XD";  // your network password
-int status = WL_IDLE_STATUS;     // the Wifi radio's status
-/************************* Adafruit.io Setup *********************************/
-
-#define AIO_SERVER      "io.adafruit.com"
-#define AIO_SERVERPORT  1883
-#define AIO_USERNAME    "SXHXC"
-#define AIO_KEY         "77491e02fca141c5935b71d9e77c9fd4"
-
-/************ Global State (you don't need to change this!) ******************/
-
-//Set up the ethernet client
-WiFiClient client;
-// Store the MQTT server, client ID, username, and password in flash memory.
-// This is required for using the Adafruit MQTT library.
-const char MQTT_SERVER[] PROGMEM    = AIO_SERVER;
-// Set a unique MQTT client ID using the AIO key + the date and time the sketch
-// was compiled (so this should be unique across multiple devices for a user,
-// alternatively you can manually set this to a GUID or other random value).
-const char MQTT_CLIENTID[] PROGMEM  = __TIME__ AIO_USERNAME;
-const char MQTT_USERNAME[] PROGMEM  = AIO_USERNAME;
-const char MQTT_PASSWORD[] PROGMEM  = AIO_KEY;
-
-Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, AIO_SERVERPORT, MQTT_CLIENTID, MQTT_USERNAME, MQTT_PASSWORD);
-
-// You don't need to change anything below this line!
-
-#define halt(s) { Serial.println(F( s )); while(1);  }
-
-/****************************** Feeds ***************************************/
-
-// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
-// Setup a feed called 'photocell' for publishing.
-const char PHOTOCELL_FEED[] PROGMEM = AIO_USERNAME "/feeds/photocell";
-Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, PHOTOCELL_FEED);
-
-// Setup a feed called 'onoff' for subscribing to changes.
-const char ONOFF_FEED[] PROGMEM = AIO_USERNAME "/feeds/onoff";
-Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, ONOFF_FEED);
-
-
-/*************************** Sketch Code ************************************/
-void setup() {
-  //Initialize serial and wait for port to open:
-   Serial.begin(115200);
-  while (!Serial) {
-    ; // wait for serial port to connect. Needed for native USB port only
-  }
-  Serial.println(F("Adafruit MQTT demo"));
-  // attempt to connect to Wifi network:
-  while ( status != WL_CONNECTED) {
-    Serial.print("Attempting to connect to WPA SSID: ");
-    Serial.println(ssid);
-    // Connect to WPA/WPA2 network:
-    status = WiFi.begin(ssid, pass);
-    // wait 5 seconds for connection:
-    delay(500);
-  }
-
-  // you're connected now, so print out the data:
-  Serial.print("You're connected to the network");
-  mqtt.subscribe(&onoffbutton);
-}
-uint32_t x=0;
-
-void loop() {
-  // Ensure the connection to the MQTT server is alive (this will make the first
-  // connection and automatically reconnect when disconnected).  See the MQTT_connect
-  // function definition further below.
-  MQTT_connect();
-
-  // this is our 'wait for incoming subscription packets' busy subloop
-  // try to spend your time here
-
-  Adafruit_MQTT_Subscribe *subscription;
-  while ((subscription = mqtt.readSubscription(3000))) {
-    if (subscription == &onoffbutton) {
-      Serial.print(F("Got: "));
-      Serial.println((char *)onoffbutton.lastread);
-    }
-  }
-
-  // Now we can publish stuff!
-  Serial.print(F("\nSending photocell val "));
-  Serial.print(x);
-  Serial.print("...");
-  if (! photocell.publish(x++)) {
-    Serial.println(F("Failed"));
-  } else {
-    Serial.println(F("OK!"));
-  }
-  //Since there's no publish's in this code, you will have to ping every few minutes at least. Uncomment the ping code
-  // ping the server to keep the mqtt connection alive
-  if(! mqtt.ping()) {
-    mqtt.disconnect();
-  }
-
-}
-
-// Function to connect and reconnect as necessary to the MQTT server.
-// Should be called in the loop function and it will take care if connecting.
-void MQTT_connect() {
-int8_t ret;
-
-  // Stop if already connected.
-  if (mqtt.connected()) {
-    return;
-  }
-
-  Serial.print("\nConnecting to MQTT... ");
-
-  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
-       Serial.println(mqtt.connectErrorString(ret));
-       Serial.println("Retrying MQTT connection in 5 seconds...");
-       mqtt.disconnect();
-       delay(1000);  // wait 5 seconds
-  }
-  Serial.println("MQTT Connected!");
+#include <SPI.h>
+#include <WiFi101.h>
+#include "Adafruit_MQTT.h"
+#include "Adafruit_MQTT_Client.h"
+
+/************************* Wifi Setup *********************************/
+
+char ssid[] = "yourNetwork";     //  your network SSID (name)
+char pass[] = "secretPassword";  // your network password
+int status = WL_IDLE_STATUS;     // the Wifi radio's status
+
+/************************* Adafruit.io Setup *********************************/
+
+#define AIO_SERVER      "io.adafruit.com"
+#define AIO_SERVERPORT  1883
+#define AIO_USERNAME    "...your AIO username (see https://accounts.adafruit.com)..."
+#define AIO_KEY         "...your AIO key..."
+
+/************ Global State (you don't need to change this!) ******************/
+
+//Set up the ethernet client
+WiFiClient client;
+// Store the MQTT server, client ID, username, and password in flash memory.
+// This is required for using the Adafruit MQTT library.
+const char MQTT_SERVER[] PROGMEM    = AIO_SERVER;
+// Set a unique MQTT client ID using the AIO key + the date and time the sketch
+// was compiled (so this should be unique across multiple devices for a user,
+// alternatively you can manually set this to a GUID or other random value).
+const char MQTT_CLIENTID[] PROGMEM  = __TIME__ AIO_USERNAME;
+const char MQTT_USERNAME[] PROGMEM  = AIO_USERNAME;
+const char MQTT_PASSWORD[] PROGMEM  = AIO_KEY;
+
+Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, AIO_SERVERPORT, MQTT_CLIENTID, MQTT_USERNAME, MQTT_PASSWORD);
+
+// You don't need to change anything below this line!
+
+#define halt(s) { Serial.println(F( s )); while(1);  }
+
+/****************************** Feeds ***************************************/
+
+// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
+// Setup a feed called 'photocell' for publishing.
+const char PHOTOCELL_FEED[] PROGMEM = AIO_USERNAME "/feeds/photocell";
+Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, PHOTOCELL_FEED);
+
+// Setup a feed called 'onoff' for subscribing to changes.
+const char ONOFF_FEED[] PROGMEM = AIO_USERNAME "/feeds/onoff";
+Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, ONOFF_FEED);
+
+
+/*************************** Sketch Code ************************************/
+void setup() {
+  //Initialize serial and wait for port to open:
+   Serial.begin(115200);
+  while (!Serial) {
+    ; // wait for serial port to connect. Needed for native USB port only
+  }
+  Serial.println(F("Adafruit MQTT demo"));
+  // attempt to connect to Wifi network:
+  while ( status != WL_CONNECTED) {
+    Serial.print("Attempting to connect to WPA SSID: ");
+    Serial.println(ssid);
+    // Connect to WPA/WPA2 network:
+    status = WiFi.begin(ssid, pass);
+    // wait 5 seconds for connection:
+    delay(500);
+  }
+
+  // you're connected now, so print out the data:
+  Serial.print("You're connected to the network");
+  mqtt.subscribe(&onoffbutton);
 }
+uint32_t x=0;
+
+void loop() {
+  // Ensure the connection to the MQTT server is alive (this will make the first
+  // connection and automatically reconnect when disconnected).  See the MQTT_connect
+  // function definition further below.
+  MQTT_connect();
+
+  // this is our 'wait for incoming subscription packets' busy subloop
+  // try to spend your time here
+
+  Adafruit_MQTT_Subscribe *subscription;
+  while ((subscription = mqtt.readSubscription(3000))) {
+    if (subscription == &onoffbutton) {
+      Serial.print(F("Got: "));
+      Serial.println((char *)onoffbutton.lastread);
+    }
+  }
+
+  // Now we can publish stuff!
+  Serial.print(F("\nSending photocell val "));
+  Serial.print(x);
+  Serial.print("...");
+  if (! photocell.publish(x++)) {
+    Serial.println(F("Failed"));
+  } else {
+    Serial.println(F("OK!"));
+  }
+  //Since there's no publish's in this code, you will have to ping every few minutes at least. Uncomment the ping code
+  // ping the server to keep the mqtt connection alive
+  if(! mqtt.ping()) {
+    mqtt.disconnect();
+  }
+
+}
+
+// Function to connect and reconnect as necessary to the MQTT server.
+// Should be called in the loop function and it will take care if connecting.
+void MQTT_connect() {
+int8_t ret;
+
+  // Stop if already connected.
+  if (mqtt.connected()) {
+    return;
+  }
+
+  Serial.print("\nConnecting to MQTT... ");
+
+  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
+       Serial.println(mqtt.connectErrorString(ret));
+       Serial.println("Retrying MQTT connection in 5 seconds...");
+       mqtt.disconnect();
+       delay(1000);  // wait 5 seconds
+  }
+  Serial.println("MQTT Connected!");
+}


Update BareMinimum.ino

Update BareMinimum.ino

Serial.println uses BASE 10 as default, there is no need to specify it.
The comment after it did not say what was the measure of the RAM - bytes, kilobytes and so on.

--- examples/BareMinimum/BareMinimum.ino
+++ examples/BareMinimum/BareMinimum.ino
@@ -7,11 +7,11 @@
   Serial.println(getPSTR("Old way to force String to Flash")); // forced to be compiled into and read
   Serial.println(F("New way to force String to Flash")); // forced to be compiled into and read
   Serial.println(F("Free RAM = ")); //F function does the same and is now a built in library, in IDE > 1.0.0
-  Serial.println(freeMemory(), DEC);  // print how much RAM is available.
+  Serial.println(freeMemory());  // print how much RAM is available in bytes.
   // print how much RAM is available.
 }

 void loop() {
   // put your main code here, to run repeatedly:

-}+}


Update quadcopter.ino

Update quadcopter.ino

--- quadcopter/quadcopter.ino
+++ quadcopter/quadcopter.ino
@@ -1,22 +1,3 @@
-/*
-    Arduino-Quadcopter - An Arduino sketch to control the hardware of a quadcopter
-    Copyright (C) <2013>  <Noah Hummel>
-
-    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 3 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, see <http://www.gnu.org/licenses/>.
-*/
-
-
 #ifndef QUADARDU
 #define QUADARDU


resend of TCP package

resend of TCP package

--- test_configSIM808.ino
+++ test_configSIM808.ino
@@ -118,9 +118,7 @@

   //get the local ip number
   //GPRS.println("AT+CIFSR");
-  if(!sim808_check_with_cmd("AT+CIFSR\r\n","OK\r\n",CMD)){
-    Serial.println("AT+CIFSR => FAIL");
-  }else Serial.println("AT+CIFSR => OK");
+  sim808_send_cmd("AT+CIFSR\r\n");
   delay(50);

   //Get the remote IP address
@@ -145,7 +143,7 @@

   timer.setInterval(60000, GetGPSLocation);
-  timer.setInterval(1000, PrintSecondsElapsed);
+  //timer.setInterval(1000, PrintSecondsElapsed);
 }

@@ -263,20 +261,21 @@

   //get the local ip number
   //GPRS.println("AT+CIFSR");
-  if(!sim808_send_cmd("AT+CIFSR\r\n")){
+  sim808_send_cmd("AT+CIFSR\r\n");

   //Get the remote IP address
   //GPRS.println("AT+CDNSGIP=\"ardugps.hopto.org\"");
   if(!sim808_check_with_cmd("AT+CDNSGIP=\"ardugps.hopto.org\"\r\n","OK\r\n",CMD)){
     Serial.println("AT+CDNSGIP => FAIL");
   }else Serial.println("AT+CDNSGIP => OK");
-  delay(5000);
+  delay(1000);

   //start a connection to TCP to URL and port
   if(!sim808_check_with_cmd("AT+CIPSTART=\"TCP\",\"ardugps.hopto.org\",6789\r\n","OK\r\n",CMD)){
     Serial.println("AT+CIPSTART => FAIL");
   }else Serial.println("AT+CIPSTART => OK");
-  delay(5000);
+  delay(1000);
+  //delay(50);

   //copy latitude and longitude to send via TCP server
   memset(data2db,'\0',sizeof(data2db));
@@ -286,17 +285,28 @@

   if(!SendDataCIPSEND(data2db,sizeof(data2db))){
     Serial.println("CIPSEND => FAIL");
+    //Try to resend
+    if(!sim808_check_with_cmd("AT+CIPSTART=\"TCP\",\"ardugps.hopto.org\",6789\r\n","OK\r\n",CMD)){
+      Serial.println("AT+CIPSTART => FAIL");
+    }else Serial.println("AT+CIPSTART => OK");
+
+    delay(1000);
+    if(!SendDataCIPSEND(data2db,sizeof(data2db))){
+    Serial.println("CIPSEND =>RESEND FAIL");
+    delay(50);
+    } else Serial.println("CIPSEND => RESEND OK");
   } else Serial.println("CIPSEND => OK");
-  delay(5000);
+  //delay(5000);
+  delay(50);

   if (!CheckConnectionStatus()) {
-    Serial.println("Uno was not connected to Server");
+    Serial.println("Uno was already disconnected from Server");
   }
   else{
     sim808_check_with_cmd("AT+CIPCLOSE\r\n", "CLOSE OK\r\n", CMD);
-    Serial.println("Uno execute disconnection from Server");
+    Serial.println("Uno have disconnected from Server");
   }
-  delay(3000);
+  //delay(3000);

 }//end procedure

@@ -312,15 +322,15 @@
   sim808_send_cmd("AT+CIPSEND=");
   itoa(len, num, 10);
   sim808_send_cmd(num);
-  if(!sim808_check_with_cmd("\r\n","\>",CMD)) {
+  if(!sim808_check_with_cmd("\r\n",">",CMD)) {
     return 0;
   }
   /*if(0 != sim808_check_with_cmd(str,"SEND OK\r\n", DEFAULT_TIMEOUT * 10 ,DATA)) {
         return 0;
   }*/
-  delay(500);
+  delay(1000);
   sim808_send_cmd(str);
-  delay(500);
+  delay(1000);
   sim808_send_End_Mark();
   if(!sim808_wait_for_resp("SEND OK\r\n", DATA, DEFAULT_TIMEOUT * 10, DEFAULT_INTERCHAR_TIMEOUT * 10)) {
       return 0;


Finetuning: Hit wait window more correctly

--- Kleineswetter/Kleineswetter.ino
+++ Kleineswetter/Kleineswetter.ino
@@ -35,6 +35,7 @@

 unsigned long connectInvainThreshold = 8 * 60 * 1000L; // some minutes
 unsigned int connectTryThreshold = 7000;
+unsigned int shiftStartSeconds = 6;

 void setup() {
   unsigned long systemStart = millis();
@@ -187,7 +188,7 @@
       serverSecondsUntilOff = greeting.substring(idx+1).toInt();

         Serial.print("Moving sleep time by ");
-        Serial.println(serverSecondsUntilOff - 8);
+        Serial.println(serverSecondsUntilOff - shiftStartSeconds);
     }

     if (systemState.serverSleepSeconds < 0 || systemState.serverSleepSeconds > 600) {
@@ -265,7 +266,7 @@
   unsigned long sleepMillis = 1;
   unsigned long correctionMillis = systemOnMillis;
   if (serverSecondsUntilOff >= 0)
-    correctionMillis += (8 - serverSecondsUntilOff) * 1000; // shift wake up time to the start of the window
+    correctionMillis += (shiftStartSeconds - serverSecondsUntilOff) * 1000; // shift wake up time to the start of the window

   if (systemState.serverSleepSeconds*1000 > correctionMillis) {
     sleepMillis = systemState.serverSleepSeconds * 1000 - correctionMillis;


Comment change.

Comment change.

--- Latency_Hardware/OSVR_Latency_hardware_firmware/streaming_reporter/streaming_reporter.ino
+++ Latency_Hardware/OSVR_Latency_hardware_firmware/streaming_reporter/streaming_reporter.ino
@@ -45,7 +45,9 @@
   // can.
   Serial.begin(115200);

-  // Set the timeout on input-string parsing to 1ms.
+  // Set the timeout on input-string parsing to 1ms, so we
+  // don't waste a lot of time waiting for the completion of
+  // numbers.
   Serial.setTimeout(1);
 }


Housekeeping

Housekeeping

Slowly learning more about Github, so doing some housekeeping on the folder structure.

--- puremodular/pureADSR/GMSN_ADSR_Code_20161023/GMSN_ADSR_Code_20161023.ino
+++ puremodular/pureADSR/GMSN_ADSR_Code_20161023/GMSN_ADSR_Code_20161023.ino
@@ -1,249 +0,0 @@
-#include "SPI.h"
-
-float aPot, aCoeff, enVal = 0, dPot, dCoeff, sPot, sCoeff, sVal, rPot, rCoeff;
-boolean gate = 0, rising = 0;
-int buttonState, lastButtonState = HIGH, loopStage = 0, x = 0;
-long lastDebounceTime = 0, debounceDelay = 500;
-
-void setup() {
-  //DAC Comms
-  SPI.begin();
-  SPI.setBitOrder(MSBFIRST);
-
-  //Pins
-  pinMode(10, OUTPUT); //DAC CS
-  pinMode(2, INPUT); //TRIGGER IN
-  pinMode(3, INPUT); //BUTTON TRIGGER
-  pinMode(4, INPUT); //Button
-  pinMode(5, INPUT); //Gate In
-  pinMode(6, INPUT); //MODE SW1
-  pinMode(7, INPUT); //MODE SW2
-  digitalWrite(10, HIGH);
-
-  //Interupts
-  attachInterrupt(digitalPinToInterrupt(2), gateOn, FALLING);
-
-}
-
-void loop() {
-
-  //Fast attack label
-attack:
-
-  //Check if not in Trap Mode
-  if (digitalRead(6) != HIGH) {
-
-    //Check if Gate is On
-    if (digitalRead(4) == LOW || digitalRead(5) == LOW) {
-
-      //Attack
-      if (rising) {
-        mcpWrite((int)enVal);
-        //delay(5);
-        if (enVal == 4095) {
-          rising = 0;
-        }
-      }
-
-      //Not Attack
-      if (!rising) {
-
-        //Check if in AR Mode and goto release
-        if (digitalRead(6) == LOW && digitalRead(7) == LOW) {
-          goto rlease;
-        }
-
-        //else continue with decay to sustain
-        dPot = map(analogRead(A2), 0, 1024, 200, 0);
-        float dCoeff = dPot / 16384;
-        sPot = map(analogRead(A1), 0, 1024, 0, 4096);
-        enVal += dCoeff * (sPot - enVal);
-        mcpWrite((int)enVal);
-      }
-    }
-
-    // If no Gate, write release values
-    else {
-
-      //Quick release label
-rlease:
-
-      rPot = map(analogRead(A0), 0, 1024, 80, 0);
-      float rCoeff = rPot / 16384;
-      enVal += rCoeff * (-100 - enVal);
-      if (enVal < 0) {
-        enVal = 0;
-      }
-      mcpWrite((int)enVal);
-    }
-
-    //Poll Trigger Button, debounce, initialise and goto Attack
-    int reading = digitalRead(4);
-
-    if ((millis() - lastDebounceTime) > debounceDelay) {
-
-      if (reading != buttonState) {
-        buttonState = reading;
-        if (buttonState == LOW) {
-          enVal = 0;
-          rising = 1;
-          rising = 1;
-          lastDebounceTime = millis();
-          goto attack;
-        }
-      }
-    }
-    lastButtonState = reading;
-
-    //Get Attack Values. Moved down here to enable fast attack
-    if (rising) {
-      aPot = map(analogRead(A3), 0, 1024, 1024, 0);
-      if (aPot == 1024) {
-        enVal = 4095;
-      } else {
-        float aCoeff = aPot / 16384;
-        enVal += aCoeff * (4311 - enVal);
-        if (enVal > 4095) {
-          enVal = 4095;
-        }
-      }
-    }
-    delayMicroseconds(50);
-
-  } else {
-    //Trap Mode
-    switch (loopStage) {
-
-      //if Up
-      case 0:
-        {
-          //if enVal < 4096
-          //write enVal
-          aPot = map(analogRead(A3), 0, 1024, 200, 0);
-          float aCoeff = aPot / 16384;
-          enVal += aCoeff * (4311 - enVal);
-          if (enVal > 4096) {
-            enVal = 4095;
-            mcpWrite((int)enVal);
-            loopStage = 1;
-            break;
-          }
-          mcpWrite((int)enVal);
-        }
-        break;
-
-      //If Hold
-      case 1:
-        {
-          //if x < dPot
-          dPot = map(analogRead(A2), 0, 1024, 0, 30000);
-          mcpWrite((int)enVal);
-          x++;
-          if (x > dPot) {
-            mcpWrite((int)enVal);
-            x = 0;
-            loopStage = 2;
-            break;
-          }
-        }
-        break;
-
-      //if Down
-      case 2:
-
-        {
-          //if enVal > 0
-          //write enVal
-          sPot = map(analogRead(A1), 0, 1024, 200, 0);
-          float sCoeff = sPot / 16384;
-          enVal += sCoeff * (-100 - enVal);
-          if (enVal < 0) {
-            enVal = 0;
-            mcpWrite((int)enVal);
-            loopStage = 3;
-            break;
-          }
-          mcpWrite((int)enVal);
-        }
-        break;
-
-      //if Off
-      case 3:
-        {
-          rPot = map(analogRead(A0), 0, 1024, 0, 32800);
-          if (x < rPot) {
-            //write hold value
-            mcpWrite((int)enVal);
-            x ++;
-          } else {
-            //else Turn on Down
-            x = 0;
-            loopStage = 0;
-            break;
-          }
-        }
-        break;
-
-    }
-
-    //Poll Trigger Button, debounce, initialise Up phase
-    int reading = digitalRead(4);
-
-    if ((millis() - lastDebounceTime) > debounceDelay) {
-
-      if (reading != buttonState) {
-        buttonState = reading;
-        if (buttonState == LOW) {
-          enVal = 0;
-          x = 0;
-          loopStage = 0;
-          lastDebounceTime = millis();
-        }
-      }
-    }
-    lastButtonState = reading;
-    delayMicroseconds(50);
-  }
-}
-
-//Interrupt routine for rising edge of Gate
-void gateOn() {
-  enVal = 0;
-  rising = 1;
-  loopStage = 0;
-  x = 0;
-}
-
-//Function for writing value to DAC. 0 = Off 4095 = Full on.
-
-void mcpWrite(int value) {
-
-  //CS
-  digitalWrite(10, LOW);
-
-  //DAC1 write
-
-  //set top 4 bits of value integer to data variable
-  byte data = value >> 8;
-  data = data & B00001111;
-  data = data | B00110000;
-  SPI.transfer(data);
-
-  data = value;
-  SPI.transfer(data);
-
-  // Set digital pin 10 HIGH
-  digitalWrite(10, HIGH);
-}
-
-//Test function for flashing the led. Value = no of flashes, time = time between flashes in mS
-void flash(int value, int time) {
-  int x = 0;
-  while (x < value) {
-    mcpWrite(4000);
-    delay(time);
-    mcpWrite(0);
-    delay(200);
-    x++;
-  }
-}


Changed Variable Names

Changed Variable Names

--- PortableAntennaRotorControl.ino
+++ PortableAntennaRotorControl.ino
@@ -76,7 +76,14 @@
 float apfiLat, apfiLon, apfiAlt, apfiVg, apfiHdg;

 boolean targetValid;
-float target[6];
+
+long  targetTime;
+float targetPosX; //lon
+float targetPosY; //lat
+float targetPosZ; //alt
+float targetVelX;
+float targetVelY;
+float targetVelZ;

 Aprs aprsObj = Aprs( "WB9SKY,KC9LIG,KC9LHW" );

@@ -447,38 +454,45 @@
   if ( aprsNewFlg ) {
     if ( DEBUG ) Serial.println( "APRS target update" );
     // Set prior target state to new APRS data
-    target[0] = aprsTime;
+    targetTime = aprsTime;
     // Initialization for computed derivatives on first valid APRS fix
     if ( targetValid ) {
-      target[6] = ( aprsAlt - lastAprsAlt ) / ( aprsTime - lastAprsTime );
+      targetVelZ = ( aprsAlt - lastAprsAlt ) / ( aprsTime - lastAprsTime );
     } else {
-      target[6] = 0.0;
+      targetVelZ = 0.0;
       targetValid = true;
     }
-    target[1] = aprsLon;
-    target[2] = aprsLat;
-    target[3] = aprsAlt;
-    target[4] = aprsVg * cos( aprsHdg * deg2rad ) * cos( aprsLat * deg2rad );
-    target[5] = aprsVg * sin( aprsHdg * deg2rad );
-    lastAprsTime = aprsTime;
+    targetPosX = aprsLon;
+    targetPosY = aprsLat;
+    targetPosZ = aprsAlt;
+    targetVelX = aprsVg * cos( aprsHdg * deg2rad ) * cos( aprsLat * deg2rad );
+    targetVelY = aprsVg * sin( aprsHdg * deg2rad );
+    targetVelZ = ( aprsAlt - lastAprsAlt ) / ( targetTime - lastAprsTime );
+    lastAprsTime = targetTime;
     lastAprsAlt = aprsAlt;
     aprsNewFlg = false;
   }

   // Update target state
   if ( targetValid ) {
-    float temp = target[0]; // old time
-    target[0] = float(now());
-    temp = target[0] - temp; // delta t
-    target[1] += ( target[4] * temp );
-    target[2] += ( target[5] * temp );
-    target[3] += ( target[6] * temp );
-  }
-//  for ( int i = 0 ; i < 6 ; i++ ) {
-//    Serial.print( target[i] );
-//    Serial.print( ":" );
-//  }
-//  Serial.println( "" );
-}
-
-
+    long temp = targetTime; // old time, same as lastAprsTime??
+    targetTime = now();
+    temp = targetTime - temp; // delta t
+    targetPosX += ( targetVelX * temp );
+    targetPosY += ( targetVelY * temp );
+    targetPosZ += ( targetVelZ * temp );
+  }
+
+  //FOR DEBUG PURPOSES
+  Serial.println(targetPosX);
+  Serial.println(targetPosY);
+  Serial.println(targetPosZ);
+  Serial.println(targetVelX);
+  Serial.println(targetVelY);
+  Serial.println(targetVelZ);
+  Serial.println(lastAprsTime);
+  Serial.println(targetTime);
+
+}
+
+


update url

update url

--- weather-clock.ino
+++ weather-clock.ino
@@ -15,7 +15,7 @@
 // if you don't want to use DNS (and reduce your sketch size)
 // use the numeric IP instead of the name for the server:
 //IPAddress server(74,125,232,128);  // numeric IP for Google (no DNS)
-char server[] = "api.wunderground.com";
+char server[] = "weatherclockserver.herokuapp.com";

 char c;
 // Initialize the Ethernet client library
@@ -55,8 +55,8 @@
   if (client.connect(server, 80)) {
     Serial.println("connected to server");
     // Make a HTTP request:
-    client.println("GET /api/" + String(WEATHER_KEY) + "/forecast/q/40.7055211,-73.9394487.json HTTP/1.1");
-    client.println("Host: api.wunderground.com");
+    client.println("GET /weather HTTP/1.1");
+    client.println("Host: weatherclockserver.herokuapp.com");
     client.println("Connection: close");
     client.println();
   }
@@ -70,19 +70,20 @@
   //   json[i] = 50;
   // }

-  // while (client.available()) {
+  // while (client.available()) {
     // client.read(json, 1000);
     // StaticJsonBuffer<10000000000> jsonBuffer;
     // JsonObject& httpRes = jsonBuffer.parseObject(c);
     // const char* temp = httpRes["hourly"];
     // Serial.println(client.available());
-
+
   // }

   while(client.available()) {
     c = client.read();
     Serial.write(c);
   }
+

   // if the server's disconnected, stop the client:
   if (!client.connected()) {
@@ -91,8 +92,9 @@
     client.stop();

     // do nothing forevermore:
+     while (true);
   }
-    while (true);
+
 }

@@ -111,4 +113,4 @@
   Serial.print("signal strength (RSSI):");
   Serial.print(rssi);
   Serial.println(" dBm");
-}+}


Add 5Hz trigger.

Add 5Hz trigger.

--- Trigger/Trigger.ino
+++ Trigger/Trigger.ino
@@ -1,16 +1,22 @@

 //
-// Generate a 9Hz signal to trigger camera and lightning
+// Generate a signal to trigger camera and lightning
 //

 // Setup
 int button = 7;
 int trigger = 13;
-int high_duration = 11;
-int low_duration = 100;
 boolean running = false;
 int button_state;
 int last_button_state = LOW;
+
+// 5 Hz
+int high_duration = 10;
+int low_duration = 190;
+
+// 9 Hz
+//int high_duration = 10;
+//int low_duration = 101;

 // Arduino setup
 void setup() {


Clean up clutter

Clean up clutter

--- TinyScreenClockAndDisplay/TinyScreenClockAndDisplay.ino
+++ TinyScreenClockAndDisplay/TinyScreenClockAndDisplay.ino
@@ -186,74 +186,6 @@
       display.print(day);
       display.print("th");
     }
-    else if (day == 4)
-  {
-    display.print("4th");
-  }
-    else if (day == 5)
-  {
-    display.print("5th");
-  }
-    else if (day == 6)
-  {
-    display.print("6th");
-  }
-    else if (day == 7)
-  {
-    display.print("7th");
-  }
-    else if (day == 8)
-  {
-    display.print("8th");
-  }
-    else if (day == 9)
-  {
-    display.print("9th");
-  }
-    else if (day == 10)
-  {
-    display.print("10th");
-  }
-    else if (day == 11)
-  {
-    display.print("11th");
-  }
-    else if (day == 12)
-  {
-    display.print("12th");
-  }
-    else if (day == 13)
-  {
-    display.print("13th");
-  }
-    else if (day == 14)
-  {
-    display.print("14th");
-  }
-    else if (day == 15)
-  {
-    display.print("15th");
-  }
-    else if (day == 16)
-  {
-    display.print("16th");
-  }
-    else if (day == 17)
-  {
-    display.print("17th");
-  }
-    else if (day == 18)
-  {
-    display.print("18th");
-  }
-    else if (day == 19)
-  {
-    display.print("19th");
-  }
-    else if (day == 20)
-  {
-    display.print("20th");
-  }
     else if (day == 21)
   {
     display.print("21st");


Update DriverMEGA.ino

Update DriverMEGA.ino

Clarified instructions on how to compile DriverMEGA.ino

--- DriverBoards/ArduinoMega64/firmware/DriverMEGA/DriverMEGA.ino
+++ DriverBoards/ArduinoMega64/firmware/DriverMEGA/DriverMEGA.ino
@@ -1,197 +1,200 @@
-/*Please do not forget to In the public interface of Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial.h
-add the following inline functions
-
-inline bool _dataAvailable() {return _rx_buffer_head != _rx_buffer_tail; }
-inline byte _peekData() { return _rx_buffer[_rx_buffer_tail]; }
-inline void _discardByte() { _rx_buffer_tail = (rx_buffer_index_t)(_rx_buffer_tail + 1) % SERIAL_RX_BUFFER_SIZE; }
-*/
-
-#include <avr/sleep.h>
-#include <avr/power.h>
-
-#define N_PATTERNS 32
-
-#define N_PORTS 10
-#define N_DIVS 10
-
-//ports A C L B K F H D G J
-
-#define COMMAND_SWITCH 0b00000000
-#define COMMAND_DURATION 0b00110000
-#define MASK_DURATION 0b00111111
-#define COMMAND_COMMITDURATIONS 0b00010000
-
-#define WAIT(a) __asm__ __volatile__ ("nop")
-#define OUTPUT_WAVE(pointer, d)  PORTA = pointer[d*N_PORTS + 0]; PORTC = pointer[d*N_PORTS + 1]; PORTL = pointer[d*N_PORTS + 2]; PORTB = pointer[d*N_PORTS + 3]; PORTK = pointer[d*N_PORTS + 4]; PORTF = pointer[d*N_PORTS + 5]; PORTH = pointer[d*N_PORTS + 6];  PORTD = pointer[d*N_PORTS + 7]; PORTG = pointer[d*N_PORTS + 8]; PORTJ = pointer[d*N_PORTS + 9]
-
-static byte bufferA[N_PATTERNS * N_DIVS * N_PORTS];
-static byte bufferB[N_PATTERNS * N_DIVS * N_PORTS];
-
-void setup()
-{
-  //set as output ports A C L B K F H D G J
-  DDRA = DDRC = DDRL = DDRB = DDRK = DDRF = DDRH = DDRD = DDRG = DDRJ = 0xFF;
-  //low signal on all of them
-  PORTA = PORTC = PORTL = PORTB = PORTK = PORTF = PORTH = PORTD = PORTG = PORTJ = 0x00;
-
-  //clear the buffers
-  for (int i = 0; i < (N_PATTERNS * N_DIVS * N_PORTS); ++i) {
-    bufferA[i] = bufferB[i] = 0;
-  }
-
-//initial pattern
-  for (int i = 0; i < (N_PORTS*N_DIVS/2); ++i){
-     bufferA[i] = 0xFF;
-  }
-
-
-  //a patterns of 01s at pin 22, for debuging and adjusting times
- /*
-  for(int i = 0; i < N_DIVS; ++i){
-    if (i % 2 == 0){
-      bufferA[i * N_PORTS] |= 0b00001000;
-    }else{
-      bufferA[i * N_PORTS] &= 0b11110111;
-    }
-  }
- */
-
-  // generate a sync signal of 40khz in pin 2
-  pinMode (2, OUTPUT);
-  noInterrupts();           // disable all interrupts
-  TCCR3A = bit (WGM10) | bit (WGM11) | bit (COM1B1); // fast PWM, clear OC1B on compare
-  TCCR3B = bit (WGM12) | bit (WGM13) | bit (CS10);   // fast PWM, no prescaler
-  OCR3A =  (F_CPU / 40000L) - 5; //should only be -1 but fine tunning with the scope determined that -5 gave 40kHz almost exactly
-  OCR3B = (F_CPU / 40000L) / 2;
-  interrupts();             // enable all interrupts
-
-  //sync in signal at pin 3
-  pinMode(3, INPUT_PULLUP); //please connect pin3 to pin 2
-
-
-  // disable everything that we do not need
-  ADCSRA = 0;  // ADC
-  power_adc_disable ();
-  power_spi_disable();
-  power_twi_disable();
-  power_timer0_disable();
-  power_usart1_disable();
-  power_usart2_disable();
-  power_usart3_disable();
-  //power_usart0_disable();
-
-  Serial.begin(115200);
-
-  byte bReceived = 0;
-  bool byteReady = false;
-  bool isSwitch = false;
-  bool isPatternForMe = false;
-  bool isDuration = false;
-  bool isCommitDurations = false;
-  byte nextMsg = 0;
-  int writtingIndex = 0;
-
-  bool emittingA = true;
-  byte* emittingPointerH = & bufferA[0];
-  byte* emittingPointerL = & bufferA[N_PORTS * N_DIVS / 2];
-  byte* emittingPointerZeroH = & bufferA[0];
-  byte* emittingPointerZeroL = & bufferA[N_PORTS * N_DIVS / 2];
-  byte* readingPointerH = & bufferB[0];
-  byte* readingPointerL = & bufferB[N_PORTS * N_DIVS / 2];
-
-  byte durations[N_PATTERNS];
-  byte durationsBuffer[N_PATTERNS];
-  for(int i = 0; i < N_PATTERNS; ++i){
-    durations[i] = durationsBuffer[i] = 0;
-  }
-  durations[0] = durationsBuffer[0] = 1;
-
-  byte currentPattern = 0;
-  byte currentPeriods = 0;
-  byte durationsPointer = 0;
-  byte currentDuration = 0;
-  bool patternComplete = false;
-  bool lastPattern = false;
-  byte nextPattern = 0;
-  byte nextDuration = 0;
-  bool returnToFirstPattern = false;
-LOOP:
-  while (PINE & 0b00100000); //wait for pin 3 (E5 Sync In) to go low
-
-  OUTPUT_WAVE(emittingPointerH, 0); byteReady = Serial._dataAvailable();
-  OUTPUT_WAVE(emittingPointerH, 1); bReceived = Serial._peekData();
-  OUTPUT_WAVE(emittingPointerH, 2); isSwitch = bReceived == COMMAND_SWITCH; isCommitDurations = bReceived == COMMAND_COMMITDURATIONS;
-  OUTPUT_WAVE(emittingPointerH, 3); isPatternForMe = (bReceived & 0b00001111) == 1; ++currentPeriods;
-  OUTPUT_WAVE(emittingPointerH, 4); nextMsg =  bReceived - 1;
-  OUTPUT_WAVE(emittingPointerL, 0); isDuration = (bReceived & MASK_DURATION) == COMMAND_DURATION; nextPattern = currentPattern + 1;
-  OUTPUT_WAVE(emittingPointerL, 1); nextDuration = durations[nextPattern];
-  OUTPUT_WAVE(emittingPointerL, 2); patternComplete = (currentPeriods == durations[currentPattern]);
-  OUTPUT_WAVE(emittingPointerL, 3); lastPattern = (currentPattern+1 == N_PATTERNS); returnToFirstPattern = nextDuration == 0;
-  OUTPUT_WAVE(emittingPointerL, 4);
-
-if (patternComplete){
-  currentPeriods = 0;
-  ++currentPattern;
-  if (lastPattern  || returnToFirstPattern){
-    currentPattern = 0;
-    emittingPointerH = emittingPointerZeroH;
-    emittingPointerL = emittingPointerZeroL;
-  }else{
-    emittingPointerH += (N_DIVS * N_PORTS);
-    emittingPointerL += (N_DIVS * N_PORTS);
-  }
-}
-
-  if (byteReady) {
-    if ( isSwitch ) {
-      Serial.write( COMMAND_SWITCH );
-      emittingA = !emittingA;
-      if (emittingA) {
-        emittingPointerH = & bufferA[0];
-        emittingPointerL = & bufferA[N_PORTS * N_DIVS / 2];
-        readingPointerH = & bufferB[0];
-        readingPointerL = & bufferB[N_PORTS * N_DIVS / 2];
-      } else {
-        emittingPointerH = & bufferB[0];
-        emittingPointerL = & bufferB[N_PORTS * N_DIVS / 2];
-        readingPointerH = & bufferA[0];
-        readingPointerL = & bufferA[N_PORTS * N_DIVS / 2];
-      }
-      emittingPointerZeroH = emittingPointerH;
-      emittingPointerZeroL = emittingPointerL;
-
-      writtingIndex = 0;
-      durationsPointer = 0;
-    } else if ( isPatternForMe ) {
-        if (writtingIndex % 2 == 0) {
-          readingPointerH[writtingIndex / 2] = bReceived & 0xF0;
-        } else {
-          readingPointerH[writtingIndex / 2] |= (bReceived >> 4);
-        }
-        ++writtingIndex;
-    } else if (isDuration){
-        Serial.write( bReceived );
-        if (durationsPointer % 4 == 0) {
-          durationsBuffer[durationsPointer / 4] = bReceived & 0b11000000;
-        } else {
-          durationsBuffer[durationsPointer / 4] |= (bReceived & 0b11000000) >> (durationsPointer % 4 * 2);
-        }
-        ++durationsPointer;
-    }else if (isCommitDurations){
-      Serial.write( bReceived );
-      for(int i = 0; i<N_PATTERNS; ++i){
-        durations[i] = durationsBuffer[i];
-      }
-      durationsPointer = 0;
-    } else {
-      Serial.write( nextMsg );
-    }
-    Serial._discardByte();
-  }
-
-  goto LOOP;
-
-}
-
-void loop() {}
-
+/* To compile this file correctly you will need to:
+
+*** add the following inline functions in the public interface (after "public:") of Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial.h
+inline bool _dataAvailable() {return _rx_buffer_head != _rx_buffer_tail; }
+inline byte _peekData() { return _rx_buffer[_rx_buffer_tail]; }
+inline void _discardByte() { _rx_buffer_tail = (rx_buffer_index_t)(_rx_buffer_tail + 1) % SERIAL_RX_BUFFER_SIZE; }
+
+*** If you are using an IDE >1.6.X you will need to compile with -O3 (optimization speed)
+
+*/
+
+#include <avr/sleep.h>
+#include <avr/power.h>
+
+#define N_PATTERNS 32
+
+#define N_PORTS 10
+#define N_DIVS 10
+
+//ports A C L B K F H D G J
+
+#define COMMAND_SWITCH 0b00000000
+#define COMMAND_DURATION 0b00110000
+#define MASK_DURATION 0b00111111
+#define COMMAND_COMMITDURATIONS 0b00010000
+
+#define WAIT(a) __asm__ __volatile__ ("nop")
+#define OUTPUT_WAVE(pointer, d)  PORTA = pointer[d*N_PORTS + 0]; PORTC = pointer[d*N_PORTS + 1]; PORTL = pointer[d*N_PORTS + 2]; PORTB = pointer[d*N_PORTS + 3]; PORTK = pointer[d*N_PORTS + 4]; PORTF = pointer[d*N_PORTS + 5]; PORTH = pointer[d*N_PORTS + 6];  PORTD = pointer[d*N_PORTS + 7]; PORTG = pointer[d*N_PORTS + 8]; PORTJ = pointer[d*N_PORTS + 9]
+
+static byte bufferA[N_PATTERNS * N_DIVS * N_PORTS];
+static byte bufferB[N_PATTERNS * N_DIVS * N_PORTS];
+
+void setup()
+{
+  //set as output ports A C L B K F H D G J
+  DDRA = DDRC = DDRL = DDRB = DDRK = DDRF = DDRH = DDRD = DDRG = DDRJ = 0xFF;
+  //low signal on all of them
+  PORTA = PORTC = PORTL = PORTB = PORTK = PORTF = PORTH = PORTD = PORTG = PORTJ = 0x00;
+
+  //clear the buffers
+  for (int i = 0; i < (N_PATTERNS * N_DIVS * N_PORTS); ++i) {
+    bufferA[i] = bufferB[i] = 0;
+  }
+
+//initial pattern
+  for (int i = 0; i < (N_PORTS*N_DIVS/2); ++i){
+     bufferA[i] = 0xFF;
+  }
+
+
+  //a patterns of 01s at pin 22, for debuging and adjusting times
+ /*
+  for(int i = 0; i < N_DIVS; ++i){
+    if (i % 2 == 0){
+      bufferA[i * N_PORTS] |= 0b00001000;
+    }else{
+      bufferA[i * N_PORTS] &= 0b11110111;
+    }
+  }
+ */
+
+  // generate a sync signal of 40khz in pin 2
+  pinMode (2, OUTPUT);
+  noInterrupts();           // disable all interrupts
+  TCCR3A = bit (WGM10) | bit (WGM11) | bit (COM1B1); // fast PWM, clear OC1B on compare
+  TCCR3B = bit (WGM12) | bit (WGM13) | bit (CS10);   // fast PWM, no prescaler
+  OCR3A =  (F_CPU / 40000L) - 5; //should only be -1 but fine tunning with the scope determined that -5 gave 40kHz almost exactly
+  OCR3B = (F_CPU / 40000L) / 2;
+  interrupts();             // enable all interrupts
+
+  //sync in signal at pin 3
+  pinMode(3, INPUT_PULLUP); //please connect pin3 to pin 2
+
+
+  // disable everything that we do not need
+  ADCSRA = 0;  // ADC
+  power_adc_disable ();
+  power_spi_disable();
+  power_twi_disable();
+  power_timer0_disable();
+  power_usart1_disable();
+  power_usart2_disable();
+  power_usart3_disable();
+  //power_usart0_disable();
+
+  Serial.begin(115200);
+
+  byte bReceived = 0;
+  bool byteReady = false;
+  bool isSwitch = false;
+  bool isPatternForMe = false;
+  bool isDuration = false;
+  bool isCommitDurations = false;
+  byte nextMsg = 0;
+  int writtingIndex = 0;
+
+  bool emittingA = true;
+  byte* emittingPointerH = & bufferA[0];
+  byte* emittingPointerL = & bufferA[N_PORTS * N_DIVS / 2];
+  byte* emittingPointerZeroH = & bufferA[0];
+  byte* emittingPointerZeroL = & bufferA[N_PORTS * N_DIVS / 2];
+  byte* readingPointerH = & bufferB[0];
+  byte* readingPointerL = & bufferB[N_PORTS * N_DIVS / 2];
+
+  byte durations[N_PATTERNS];
+  byte durationsBuffer[N_PATTERNS];
+  for(int i = 0; i < N_PATTERNS; ++i){
+    durations[i] = durationsBuffer[i] = 0;
+  }
+  durations[0] = durationsBuffer[0] = 1;
+
+  byte currentPattern = 0;
+  byte currentPeriods = 0;
+  byte durationsPointer = 0;
+  byte currentDuration = 0;
+  bool patternComplete = false;
+  bool lastPattern = false;
+  byte nextPattern = 0;
+  byte nextDuration = 0;
+  bool returnToFirstPattern = false;
+LOOP:
+  while (PINE & 0b00100000); //wait for pin 3 (E5 Sync In) to go low
+
+  OUTPUT_WAVE(emittingPointerH, 0); byteReady = Serial._dataAvailable();
+  OUTPUT_WAVE(emittingPointerH, 1); bReceived = Serial._peekData();
+  OUTPUT_WAVE(emittingPointerH, 2); isSwitch = bReceived == COMMAND_SWITCH; isCommitDurations = bReceived == COMMAND_COMMITDURATIONS;
+  OUTPUT_WAVE(emittingPointerH, 3); isPatternForMe = (bReceived & 0b00001111) == 1; ++currentPeriods;
+  OUTPUT_WAVE(emittingPointerH, 4); nextMsg =  bReceived - 1;
+  OUTPUT_WAVE(emittingPointerL, 0); isDuration = (bReceived & MASK_DURATION) == COMMAND_DURATION; nextPattern = currentPattern + 1;
+  OUTPUT_WAVE(emittingPointerL, 1); nextDuration = durations[nextPattern];
+  OUTPUT_WAVE(emittingPointerL, 2); patternComplete = (currentPeriods == durations[currentPattern]);
+  OUTPUT_WAVE(emittingPointerL, 3); lastPattern = (currentPattern+1 == N_PATTERNS); returnToFirstPattern = nextDuration == 0;
+  OUTPUT_WAVE(emittingPointerL, 4);
+
+if (patternComplete){
+  currentPeriods = 0;
+  ++currentPattern;
+  if (lastPattern  || returnToFirstPattern){
+    currentPattern = 0;
+    emittingPointerH = emittingPointerZeroH;
+    emittingPointerL = emittingPointerZeroL;
+  }else{
+    emittingPointerH += (N_DIVS * N_PORTS);
+    emittingPointerL += (N_DIVS * N_PORTS);
+  }
+}
+
+  if (byteReady) {
+    if ( isSwitch ) {
+      Serial.write( COMMAND_SWITCH );
+      emittingA = !emittingA;
+      if (emittingA) {
+        emittingPointerH = & bufferA[0];
+        emittingPointerL = & bufferA[N_PORTS * N_DIVS / 2];
+        readingPointerH = & bufferB[0];
+        readingPointerL = & bufferB[N_PORTS * N_DIVS / 2];
+      } else {
+        emittingPointerH = & bufferB[0];
+        emittingPointerL = & bufferB[N_PORTS * N_DIVS / 2];
+        readingPointerH = & bufferA[0];
+        readingPointerL = & bufferA[N_PORTS * N_DIVS / 2];
+      }
+      emittingPointerZeroH = emittingPointerH;
+      emittingPointerZeroL = emittingPointerL;
+
+      writtingIndex = 0;
+      durationsPointer = 0;
+    } else if ( isPatternForMe ) {
+        if (writtingIndex % 2 == 0) {
+          readingPointerH[writtingIndex / 2] = bReceived & 0xF0;
+        } else {
+          readingPointerH[writtingIndex / 2] |= (bReceived >> 4);
+        }
+        ++writtingIndex;
+    } else if (isDuration){
+        Serial.write( bReceived );
+        if (durationsPointer % 4 == 0) {
+          durationsBuffer[durationsPointer / 4] = bReceived & 0b11000000;
+        } else {
+          durationsBuffer[durationsPointer / 4] |= (bReceived & 0b11000000) >> (durationsPointer % 4 * 2);
+        }
+        ++durationsPointer;
+    }else if (isCommitDurations){
+      Serial.write( bReceived );
+      for(int i = 0; i<N_PATTERNS; ++i){
+        durations[i] = durationsBuffer[i];
+      }
+      durationsPointer = 0;
+    } else {
+      Serial.write( nextMsg );
+    }
+    Serial._discardByte();
+  }
+
+  goto LOOP;
+
+}
+
+void loop() {}
+