Simplify the Offset command

Simplify the Offset command

Changing value to INT has introduced a number of bugs. The l & t
commands cause a freeze. Have tried various casting techniques for
'value' but no joy yet.

--- examples/RF12/RF12demo/RF12demo.ino
+++ examples/RF12/RF12demo/RF12demo.ino
@@ -85,9 +85,9 @@

 unsigned int frequency;
 static RF12Config config;
-char revP = 94; // Symbol ^ to indicate direction of frequency offset
 static char cmd;
-static byte value, stack[RF12_MAXDATA+4], top, sendLen, dest, sticky, revF = 0;
+static int value;
+static byte stack[RF12_MAXDATA+4], top, sendLen, dest;
 static byte testbuf[RF12_MAXDATA], testCounter, useHex;
 static byte band;

@@ -139,7 +139,7 @@
   static word bands[4] = { 0, 430, 860, 900 }; // 315, 433, 864, 915 Mhz
   band = config.nodeId >> 6;
   long wk = frequency;                                        // 96 - 3903 is the range of values supported by the RFM12B
-  wk = wk * (band * 25);                                             // Freqency changes larger in higher bands
+  wk = wk * (band * 25);                                      // Actual freqency changes are larger in higher bands
   long characteristic = wk/10000;
   addInt(config.msg, characteristic + bands[band]);
   byte pos = strlen(config.msg);
@@ -597,8 +597,8 @@
   "Available commands:" "\n"
   "  <nn> i     - set node ID (standard node ids are 1..30)" "\n"
   "  <n> b      - set MHz band (4 = 433, 8 = 868, 9 = 915)" "\n"
-  "  <nnn> o    - Change frequency offset within the band above" "\n"
-  "               values > 99 are sticky, 255 changes direction" "\n"
+  "  <nnnn> o   - Change frequency offset within the band above" "\n"
+  "               96 - 3903 is the range supported by the RFM12B" "\n"
   "  <nnn> g    - set network group (RFM12 only allows 212, 0 = any)" "\n"
   "  <n> c      - set collect mode (advanced, normally 0)" "\n"
   "  t          - broadcast max-size test packet, request ack" "\n"
@@ -675,7 +675,7 @@
            config.nodeId = (config.nodeId & 0xE0) + (value & 0x1F);
            saveConfig();
         } else {
-           showHelp();
+           Serial.println("\rInvalid");
         }
         break;
       case 'b': // set band: 4 = 433, 8 = 868, 9 = 915
@@ -685,32 +685,29 @@
          frequency = 1600;
          saveConfig();
         } else {
-            showHelp();
+            Serial.println("\rInvalid");
         }
         break;
       case 'o': // Increment frequency within band
-          if (value == 255) {
-            revF = !revF;
-            revP = revP ^ 40;   // Flip the indicator
-            value=0;
-          }
-
           Serial.print(frequency);
-          Serial.print(revP);
 ///
 /// It is important that you keep within your countries ISM spectrum management guidelines
 /// i.e. allowable frequencies and their use when selecting your operating frequencies.
 ///
-          if ((value) || (sticky)) {
-            if (!value) value = sticky;
-            if (value > 99) sticky = value; else sticky=0;     // Make values over 99 sticky
-            if (!revF) frequency = frequency + value; else frequency = frequency - value;
-            if (frequency < 96) frequency = 3903;  // 96 - 3903 is the range of values supported by the RFM12B
-            if (frequency > 3903) frequency = 96;
-            Serial.println(frequency);
-            saveConfig();
-          } else
-          Serial.println();
+          if (value) {
+            if ((value > 95) && (value < 3904)) {  // 96 - 3903 is the range of values supported by the RFM12B
+              frequency = value;
+              Serial.print(">");
+              Serial.println(frequency);
+              saveConfig();
+            }
+            else {
+              Serial.println("\rInvalid");
+            }
+          }
+          else {
+            Serial.println();
+          }
         break;
       case 'g': // set network group
         config.group = value;
@@ -745,7 +742,8 @@
         memcpy(testbuf, stack, top);
         break;
       case 'l': // turn activity LED on or off
-        activityLed(value);
+        if (value) activityLed(1);
+        else activityLed(0);
         break;
 #if not defined(__AVR_ATtiny84__) || not defined(__AVR_ATtiny44__)
       case 'f': // send FS20 command: <hchi>,<hclo>,<addr>,<cmd>f


latest lib

latest lib

--- gamecubecontroller.ino
+++ gamecubecontroller.ino
@@ -63,19 +63,19 @@
 }

 const uint8_t reportDescription[] = {
-   USB_HID_MOUSE_REPORT_DESCRIPTOR(USB_HID_MOUSE_REPORT_ID),
-   USB_HID_KEYBOARD_REPORT_DESCRIPTOR(USB_HID_KEYBOARD_REPORT_ID),
-   USB_HID_JOYSTICK_REPORT_DESCRIPTOR(USB_HID_JOYSTICK_REPORT_ID,
-        USB_HID_FEATURE_REPORT_DESCRIPTOR(FEATURE_REPORT_SIZE)),
+   HID_MOUSE_REPORT_DESCRIPTOR(),
+   HID_KEYBOARD_REPORT_DESCRIPTOR(),
+   HID_JOYSTICK_REPORT_DESCRIPTOR(HID_JOYSTICK_REPORT_ID,
+        HID_FEATURE_REPORT_DESCRIPTOR(FEATURE_REPORT_SIZE)),
 };

 uint8_t featureReport[FEATURE_REPORT_SIZE];
 uint8_t featureBuffer[HID_BUFFER_ALLOCATE_SIZE(FEATURE_REPORT_SIZE,1)];
-volatile HIDBuffer_t fb { featureBuffer, HID_BUFFER_SIZE(FEATURE_REPORT_SIZE,1), USB_HID_JOYSTICK_REPORT_ID };
+volatile HIDBuffer_t fb { featureBuffer, HID_BUFFER_SIZE(FEATURE_REPORT_SIZE,1), HID_JOYSTICK_REPORT_ID };

 void setup() {
-  HID.begin(reportDescription,sizeof(reportDescription));
-  HID.setBuffers(HID_REPORT_TYPE_FEATURE, &fb, 1);
+  USB.begin(reportDescription,sizeof(reportDescription));
+  USB.setBuffers(HID_REPORT_TYPE_FEATURE, &fb, 1);
   for (int i=0; i<numIndicators; i++)
     pinMode(indicatorLEDs[i], OUTPUT);
   pinMode(downButton, INPUT_PULLDOWN);


Update Play RAD

Update Play RAD

This updates the Play RAD example to work with the current library
version.

--- examples/OPL2AudioBoard/PlayRAD/PlayRAD.ino
+++ examples/OPL2AudioBoard/PlayRAD/PlayRAD.ino
@@ -52,8 +52,8 @@

 // Different note F-numbers stored in program memory.
 const short noteFrequencies[12] PROGMEM = {
-  0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5,
-  0x202, 0x220, 0x241, 0x263, 0x287, 0x2AE
+	0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5,
+	0x202, 0x220, 0x241, 0x263, 0x287, 0x2AE
 };

@@ -86,28 +86,28 @@

 void setup() {
-  SD.begin(7);
-
-  // Load one of the included RAD files.
-  loadRadFile("adlibsp.rad");
-  // loadRadFile("shoot.rad");
-  // loadRadFile("action.rad");
-
-  initPlayer();
+	SD.begin(7);
+
+	// Load one of the included RAD files.
+	loadRadFile("adlibsp.rad");
+	// loadRadFile("shoot.rad");
+	// loadRadFile("action.rad");
+
+	initPlayer();
 }

 void loop() {
-  unsigned long time = millis();
-
-  if (order < songLength) {
-    playMusic();
-  }
-
-  // Calculate delay based on song speed and time spent in player routine.
-  int timeSpent = millis() - time;
-  int wait = max(0, (hasSlowTimer ? 55 : 20) - timeSpent);
-  delay(wait);
+	unsigned long time = millis();
+
+	if (order < songLength) {
+		playMusic();
+	}
+
+	// Calculate delay based on song speed and time spent in player routine.
+	int timeSpent = millis() - time;
+	int wait = max(0, (hasSlowTimer ? 55 : 20) - timeSpent);
+	delay(wait);
 }

@@ -115,23 +115,21 @@
  * Load instrument data for the given channel.
  */
 void setInstrument(byte channel, byte instrumentIndex) {
-  byte registerOffset;
-
-  registerOffset = opl2.getRegisterOffset(channel, CARRIER);
-  opl2.setRegister(0x20 + registerOffset, instruments[instrumentIndex][0]);
-  opl2.setRegister(0x40 + registerOffset, instruments[instrumentIndex][2]);
-  opl2.setRegister(0x60 + registerOffset, instruments[instrumentIndex][4]);
-  opl2.setRegister(0x80 + registerOffset, instruments[instrumentIndex][6]);
-  opl2.setRegister(0xE0 + registerOffset, instruments[instrumentIndex][9] & 0x0F);
-
-  registerOffset = opl2.getRegisterOffset(channel, MODULATOR);
-  opl2.setRegister(0x20 + registerOffset, instruments[instrumentIndex][1]);
-  opl2.setRegister(0x40 + registerOffset, instruments[instrumentIndex][3]);
-  opl2.setRegister(0x60 + registerOffset, instruments[instrumentIndex][5]);
-  opl2.setRegister(0x80 + registerOffset, instruments[instrumentIndex][7]);
-  opl2.setRegister(0xE0 + registerOffset, (instruments[instrumentIndex][9] & 0xF0) >> 4);
-
-  opl2.setRegister(0xC0 + channel, instruments[instrumentIndex][8]);
+	byte registerOffset;
+
+	opl2.setOperatorRegister(0x20, channel, CARRIER, instruments[instrumentIndex][0]);
+	opl2.setOperatorRegister(0x40, channel, CARRIER, instruments[instrumentIndex][2]);
+	opl2.setOperatorRegister(0x60, channel, CARRIER, instruments[instrumentIndex][4]);
+	opl2.setOperatorRegister(0x80, channel, CARRIER, instruments[instrumentIndex][6]);
+	opl2.setOperatorRegister(0xE0, channel, CARRIER, instruments[instrumentIndex][9] & 0x0F);
+
+	opl2.setOperatorRegister(0x20, channel, MODULATOR, instruments[instrumentIndex][1]);
+	opl2.setOperatorRegister(0x40, channel, MODULATOR, instruments[instrumentIndex][3]);
+	opl2.setOperatorRegister(0x60, channel, MODULATOR, instruments[instrumentIndex][5]);
+	opl2.setOperatorRegister(0x80, channel, MODULATOR, instruments[instrumentIndex][7]);
+	opl2.setOperatorRegister(0xE0, channel, MODULATOR, (instruments[instrumentIndex][9] & 0xF0) >> 4);
+
+	opl2.setChannelRegister(0xC0, channel, instruments[instrumentIndex][8]);
 }

@@ -139,10 +137,10 @@
  * Play a note on the given channel.
  */
 void playNote(byte channel, byte octave, byte note) {
-  opl2.setKeyOn(channel, false);
-  opl2.setBlock(channel, octave + (note / 12));
-  opl2.setFNumber(channel, pgm_read_word_near(noteFrequencies + (note % 12)));
-  opl2.setKeyOn(channel, true);
+	opl2.setKeyOn(channel, false);
+	opl2.setBlock(channel, octave + (note / 12));
+	opl2.setFNumber(channel, pgm_read_word_near(noteFrequencies + (note % 12)));
+	opl2.setKeyOn(channel, true);
 }

@@ -150,29 +148,29 @@
  * Slide the pitch of a channel by a given F-number amount and return the new block + F-number.
  */
 unsigned int pitchAdjust(byte channel, short amount) {
-  byte block = opl2.getBlock(channel);
-  short fNumber = opl2.getFNumber(channel);
-  fNumber = max(0x000, min(fNumber + amount, 0x3FF));
-
-  // Drop one octave (if possible) when the F-number drops below octave minimum.
-  if (fNumber < FNUMBER_MIN) {
-    if (block > 0) {
-      block --;
-      fNumber = FNUMBER_MAX - (FNUMBER_MIN - fNumber);
-    }
-
-  // Increase one octave (if possible) when the F-number reaches above octave maximum.
-  } else if (fNumber > FNUMBER_MAX) {
-    if (block < 7) {
-      block ++;
-      fNumber = FNUMBER_MIN + (fNumber - FNUMBER_MAX);
-    }
-  }
-
-  // Set new octave and frequency and return combind octave + F-number value.
-  opl2.setBlock(channel, block);
-  opl2.setFNumber(channel, fNumber);
-  return (block << 12) + fNumber;
+	byte block = opl2.getBlock(channel);
+	short fNumber = opl2.getFNumber(channel);
+	fNumber = max(0x000, min(fNumber + amount, 0x3FF));
+
+	// Drop one octave (if possible) when the F-number drops below octave minimum.
+	if (fNumber < FNUMBER_MIN) {
+		if (block > 0) {
+			block --;
+			fNumber = FNUMBER_MAX - (FNUMBER_MIN - fNumber);
+		}
+
+	// Increase one octave (if possible) when the F-number reaches above octave maximum.
+	} else if (fNumber > FNUMBER_MAX) {
+		if (block < 7) {
+			block ++;
+			fNumber = FNUMBER_MIN + (fNumber - FNUMBER_MAX);
+		}
+	}
+
+	// Set new octave and frequency and return combind octave + F-number value.
+	opl2.setBlock(channel, block);
+	opl2.setFNumber(channel, fNumber);
+	return (block << 12) + fNumber;
 }

@@ -181,29 +179,29 @@
  * destination pitch is reached.
  */
 void pitchAdjustToNote(byte channel) {
-  if (pitchSlideDest[channel] == 0x0000) return;
-
-  unsigned int slidePos = opl2.getBlock(channel) << 12;
-  slidePos += opl2.getFNumber(channel);
-
-  // Slide pitch up and compensate any overshoot.
-  if (slidePos < pitchSlideDest[channel]) {
-    slidePos = pitchAdjust(channel, pitchSlideSpeed[channel]);
-    if (slidePos >= pitchSlideDest[channel]) {
-      opl2.setBlock  (channel, (pitchSlideDest[channel] & 0xF000) >> 12);
-      opl2.setFNumber(channel,  pitchSlideDest[channel] & 0x0FFF);
-      pitchSlideDest[channel] = 0x0000;
-    }
-
-  // Slide pitch down and compensate for any undershoot.
-  } else if (slidePos > pitchSlideDest[channel]) {
-    slidePos = pitchAdjust(channel, -pitchSlideSpeed[channel]);
-    if (slidePos <= pitchSlideDest[channel]) {
-      opl2.setBlock  (channel, (pitchSlideDest[channel] & 0xF000) >> 12);
-      opl2.setFNumber(channel,  pitchSlideDest[channel] & 0x0FFF);
-      pitchSlideDest[channel] = 0x0000;
-    }
-  }
+	if (pitchSlideDest[channel] == 0x0000) return;
+
+	unsigned int slidePos = opl2.getBlock(channel) << 12;
+	slidePos += opl2.getFNumber(channel);
+
+	// Slide pitch up and compensate any overshoot.
+	if (slidePos < pitchSlideDest[channel]) {
+		slidePos = pitchAdjust(channel, pitchSlideSpeed[channel]);
+		if (slidePos >= pitchSlideDest[channel]) {
+			opl2.setBlock  (channel, (pitchSlideDest[channel] & 0xF000) >> 12);
+			opl2.setFNumber(channel,  pitchSlideDest[channel] & 0x0FFF);
+			pitchSlideDest[channel] = 0x0000;
+		}
+
+	// Slide pitch down and compensate for any undershoot.
+	} else if (slidePos > pitchSlideDest[channel]) {
+		slidePos = pitchAdjust(channel, -pitchSlideSpeed[channel]);
+		if (slidePos <= pitchSlideDest[channel]) {
+			opl2.setBlock  (channel, (pitchSlideDest[channel] & 0xF000) >> 12);
+			opl2.setFNumber(channel,  pitchSlideDest[channel] & 0x0FFF);
+			pitchSlideDest[channel] = 0x0000;
+		}
+	}
 }

@@ -211,15 +209,15 @@
  * Adjust the volume of a channel by the gven amount.
  */
 void volumeAdjust(byte channel, byte amount) {
-  byte volume = opl2.getVolume(channel, CARRIER);
-
-  if (amount > 0 && amount < 50) {
-    volume = min(63, volume + amount);
-  } else if (amount > 50 && amount < 100) {
-    volume = max(0, volume - (amount - 50));
-  }
-
-  opl2.setVolume(channel, CARRIER, volume);
+	byte volume = opl2.getVolume(channel, CARRIER);
+
+	if (amount > 0 && amount < 50) {
+		volume = min(63, volume + amount);
+	} else if (amount > 50 && amount < 100) {
+		volume = max(0, volume - (amount - 50));
+	}
+
+	opl2.setVolume(channel, CARRIER, volume);
 }

@@ -227,42 +225,42 @@
  * Read the next line in the pattern from the current file position.
  */
 void readLine() {
-  // Reset note data on each channel.
-  for (byte i = 0; i < 9; i ++) {
-    channelNote[i] = 0x0000;
-  }
-
-  // If the previous line was the last line of the pattern then we're done.
-  if (endOfPattern) {
-    return;
-  }
-
-  // If the next line number does not match our current line then it's empty
-  // so there's nothing to do for us here.
-  byte lineNumber = radFile.read();
-  if ((lineNumber & 0x3F) != line) {
-    radFile.seekSet(radFile.curPosition() - 1);
-    return;
-  }
-
-  // Set end of pattern marker if this is the last line in the pattern.
-  endOfPattern = lineNumber & 0x80;
-
-  // Read note and effect data for each channel.
-  bool isLastChannel = false;
-  do {
-    byte channelNumber = radFile.read();
-    isLastChannel = channelNumber & 0x80;
-    channelNumber = channelNumber & 0x0F;
-
-    channelNote[channelNumber]  = radFile.read() << 8;
-    channelNote[channelNumber] += radFile.read();
-    if (channelNote[channelNumber] & 0x000F) {
-      efftectParameter[channelNumber] = radFile.read();
-    } else {
-      efftectParameter[channelNumber] = 0x00;
-    }
-  } while(!isLastChannel);
+	// Reset note data on each channel.
+	for (byte i = 0; i < 9; i ++) {
+		channelNote[i] = 0x0000;
+	}
+
+	// If the previous line was the last line of the pattern then we're done.
+	if (endOfPattern) {
+		return;
+	}
+
+	// If the next line number does not match our current line then it's empty
+	// so there's nothing to do for us here.
+	byte lineNumber = radFile.read();
+	if ((lineNumber & 0x3F) != line) {
+		radFile.seekSet(radFile.curPosition() - 1);
+		return;
+	}
+
+	// Set end of pattern marker if this is the last line in the pattern.
+	endOfPattern = lineNumber & 0x80;
+
+	// Read note and effect data for each channel.
+	bool isLastChannel = false;
+	do {
+		byte channelNumber = radFile.read();
+		isLastChannel = channelNumber & 0x80;
+		channelNumber = channelNumber & 0x0F;
+
+		channelNote[channelNumber]  = radFile.read() << 8;
+		channelNote[channelNumber] += radFile.read();
+		if (channelNote[channelNumber] & 0x000F) {
+			efftectParameter[channelNumber] = radFile.read();
+		} else {
+			efftectParameter[channelNumber] = 0x00;
+		}
+	} while(!isLastChannel);
 }

@@ -272,28 +270,28 @@
  * reached we may loop back to the beginning if loopSong is true.
  */
 void nextOrder(byte startLine = 0) {
-  // Increment order and read patternIndex, may loop song when we've reached the end.
-  order ++;
-  if (order >= songLength && loopSong) {
-    order = 0;
-  }
-  radFile.seekSet(orderListOffset + order);
-  byte patternIndex = radFile.read();
-
-  // If bit 7 is set then we're looking at an order jump.
-  if (patternIndex & 0x80) {
-    order = patternIndex & 0x7F;
-    radFile.seekSet(orderListOffset + order);
-    patternIndex = radFile.read();
-  }
-
-  // Set file offset to the start of the next order and skip lines until we're
-  // at the requested startLine.
-  radFile.seekSet(patternOffsets[patternIndex]);
-  endOfPattern = false;
-  for (line = 0; line < startLine; line ++) {
-    readLine();
-  }
+	// Increment order and read patternIndex, may loop song when we've reached the end.
+	order ++;
+	if (order >= songLength && loopSong) {
+		order = 0;
+	}
+	radFile.seekSet(orderListOffset + order);
+	byte patternIndex = radFile.read();
+
+	// If bit 7 is set then we're looking at an order jump.
+	if (patternIndex & 0x80) {
+		order = patternIndex & 0x7F;
+		radFile.seekSet(orderListOffset + order);
+		patternIndex = radFile.read();
+	}
+
+	// Set file offset to the start of the next order and skip lines until we're
+	// at the requested startLine.
+	radFile.seekSet(patternOffsets[patternIndex]);
+	endOfPattern = false;
+	for (line = 0; line < startLine; line ++) {
+		readLine();
+	}
 }

@@ -301,16 +299,16 @@
  * Initialize the OPL2 board and load the first line of the first order.
  */
 void initPlayer() {
-  opl2.begin();
-  opl2.setWaveFormSelect(true);
-  opl2.setPercussion(false);
-  speed = initialSpeed;
-
-  order = -1;
-  line = 0;
-  tick = 0;
-  nextOrder();
-  readLine();
+	opl2.begin();
+	opl2.setWaveFormSelect(true);
+	opl2.setPercussion(false);
+	speed = initialSpeed;
+
+	order = -1;
+	line = 0;
+	tick = 0;
+	nextOrder();
+	readLine();
 }

@@ -318,104 +316,104 @@
  * Process one tick of the current line and advance the song if needed.
  */
 void playMusic() {
-  for (byte channel = 0; channel < 9; channel ++) {
-    unsigned short channelData = channelNote[channel];
-    byte effect = channelData & 0x000F;
-
-    if (tick == 0) {
-      byte instrument = ((channelData & 0x8000) >> 11) + ((channelData & 0x00F0) >> 4);
-      byte octave     = (channelData & 0x7000) >> 12;
-      byte note       = (channelData & 0x0F00) >> 8;
-
-      // Set instrument.
-      if (instrument > 0) {
-        setInstrument(channel, instrument - 1);
-      }
-
-      // Stop note.
-      if (note == 0x0F) {
-        opl2.setKeyOn(channel, false);
-      }
-
-      // Trigger note.
-      else if (note && effect != EFFECT_NOTE_SLIDE_TO) {
-        playNote(channel, octave, note);
-      }
-
-      // Process line effects.
-      switch(effect) {
-        case EFFECT_NOTE_SLIDE_TO: {
-          if (note > 0x00 && note < 0x0F) {
-            pitchSlideDest[channel]  = (octave + (note / 12)) << 12;
-            pitchSlideDest[channel] += pgm_read_word_near(noteFrequencies + (note % 12));
-            pitchSlideSpeed[channel] = efftectParameter[channel];
-          }
-          break;
-        }
-
-        case EFFECT_SET_VOLUME: {
-          opl2.setVolume(channel, CARRIER, max(0, min(64 - efftectParameter[channel], 63)));
-          break;
-        }
-
-        case EFFECT_PATTERN_BREAK: {
-          patternBreak = efftectParameter[channel];
-          break;
-        }
-
-        case EFFECT_SET_SPEED: {
-          speed = efftectParameter[channel];
-          break;
-        }
-      }
-    }
-
-    // Process tick effects.
-    switch(effect) {
-      case EFFECT_NOTE_SLIDE_UP: {
-        pitchAdjust(channel, efftectParameter[channel]);
-        break;
-      }
-
-      case EFFECT_NOTE_SLIDE_DOWN: {
-        pitchAdjust(channel, -efftectParameter[channel]);
-        break;
-      }
-
-      case EFFECT_NOTE_SLIDE_VOLUME: {
-        pitchAdjustToNote(channel);
-        volumeAdjust(channel, efftectParameter[channel]);
-        break;
-      }
-
-      case EFFECT_NOTE_SLIDE_TO: {
-        pitchAdjustToNote(channel);
-        break;
-      }
-
-      case EFFECT_VOLUME_SLIDE: {
-        volumeAdjust(channel, efftectParameter[channel]);
-        break;
-      }
-    }
-  }
-
-  // Advance song.
-  tick = (tick + 1) % speed;
-  if (tick == 0) {
-    if (patternBreak == 0xFF) {
-      line = (line + 1) % 64;
-      if (line == 0) {
-        nextOrder();
-      }
-    } else {
-      line = patternBreak;
-      patternBreak = 0xFF;
-      nextOrder();
-    }
-
-    readLine();
-  }
+	for (byte channel = 0; channel < 9; channel ++) {
+		unsigned short channelData = channelNote[channel];
+		byte effect = channelData & 0x000F;
+
+		if (tick == 0) {
+			byte instrument = ((channelData & 0x8000) >> 11) + ((channelData & 0x00F0) >> 4);
+			byte octave     = (channelData & 0x7000) >> 12;
+			byte note       = (channelData & 0x0F00) >> 8;
+
+			// Set instrument.
+			if (instrument > 0) {
+				setInstrument(channel, instrument - 1);
+			}
+
+			// Stop note.
+			if (note == 0x0F) {
+				opl2.setKeyOn(channel, false);
+			}
+
+			// Trigger note.
+			else if (note && effect != EFFECT_NOTE_SLIDE_TO) {
+				playNote(channel, octave, note);
+			}
+
+			// Process line effects.
+			switch(effect) {
+				case EFFECT_NOTE_SLIDE_TO: {
+					if (note > 0x00 && note < 0x0F) {
+						pitchSlideDest[channel]  = (octave + (note / 12)) << 12;
+						pitchSlideDest[channel] += pgm_read_word_near(noteFrequencies + (note % 12));
+						pitchSlideSpeed[channel] = efftectParameter[channel];
+					}
+					break;
+				}
+
+				case EFFECT_SET_VOLUME: {
+					opl2.setVolume(channel, CARRIER, max(0, min(64 - efftectParameter[channel], 63)));
+					break;
+				}
+
+				case EFFECT_PATTERN_BREAK: {
+					patternBreak = efftectParameter[channel];
+					break;
+				}
+
+				case EFFECT_SET_SPEED: {
+					speed = efftectParameter[channel];
+					break;
+				}
+			}
+		}
+
+		// Process tick effects.
+		switch(effect) {
+			case EFFECT_NOTE_SLIDE_UP: {
+				pitchAdjust(channel, efftectParameter[channel]);
+				break;
+			}
+
+			case EFFECT_NOTE_SLIDE_DOWN: {
+				pitchAdjust(channel, -efftectParameter[channel]);
+				break;
+			}
+
+			case EFFECT_NOTE_SLIDE_VOLUME: {
+				pitchAdjustToNote(channel);
+				volumeAdjust(channel, efftectParameter[channel]);
+				break;
+			}
+
+			case EFFECT_NOTE_SLIDE_TO: {
+				pitchAdjustToNote(channel);
+				break;
+			}
+
+			case EFFECT_VOLUME_SLIDE: {
+				volumeAdjust(channel, efftectParameter[channel]);
+				break;
+			}
+		}
+	}
+
+	// Advance song.
+	tick = (tick + 1) % speed;
+	if (tick == 0) {
+		if (patternBreak == 0xFF) {
+			line = (line + 1) % 64;
+			if (line == 0) {
+				nextOrder();
+			}
+		} else {
+			line = patternBreak;
+			patternBreak = 0xFF;
+			nextOrder();
+		}
+
+		readLine();
+	}
 }

@@ -423,36 +421,36 @@
  * Load the given RAD file from SD card.
  */
 void loadRadFile(char* fileName) {
-  radFile.open(fileName);
-
-  radFile.seekSet(0x11);
-  byte songProps = radFile.read();
-  hasSlowTimer = (songProps & 0x40) != 0x00;
-  initialSpeed = songProps & 0x1F;
-
-  // Skip song description block if available.
-  if ((songProps & 0x80) != 0x00) {
-    while (radFile.read() != 0x00);
-  }
-
-  // Read instruments.
-  byte instrumentNum = 0;
-  while ((instrumentNum = radFile.read()) != 0x00) {
-    instrumentNum --;
-    for (byte i = 0; i < 9; i ++) {
-      instruments[instrumentNum][i] = radFile.read();
-    }
-    instruments[instrumentNum][9]  =  radFile.read() & 0x07;
-    instruments[instrumentNum][9] += (radFile.read() & 0x07) << 4;
-  }
-
-  // Read song length, order list offset and skip order list.
-  songLength = radFile.read();
-  orderListOffset = radFile.curPosition();
-  radFile.seekSet(orderListOffset + songLength);
-
-  // Read pattern offsets.
-  for (byte i = 0; i < 32; i++) {
-    patternOffsets[i] = radFile.read() + (radFile.read() << 8);
-  }
-}
+	radFile.open(fileName);
+
+	radFile.seekSet(0x11);
+	byte songProps = radFile.read();
+	hasSlowTimer = (songProps & 0x40) != 0x00;
+	initialSpeed = songProps & 0x1F;
+
+	// Skip song description block if available.
+	if ((songProps & 0x80) != 0x00) {
+		while (radFile.read() != 0x00);
+	}
+
+	// Read instruments.
+	byte instrumentNum = 0;
+	while ((instrumentNum = radFile.read()) != 0x00) {
+		instrumentNum --;
+		for (byte i = 0; i < 9; i ++) {
+			instruments[instrumentNum][i] = radFile.read();
+		}
+		instruments[instrumentNum][9]  =  radFile.read() & 0x07;
+		instruments[instrumentNum][9] += (radFile.read() & 0x07) << 4;
+	}
+
+	// Read song length, order list offset and skip order list.
+	songLength = radFile.read();
+	orderListOffset = radFile.curPosition();
+	radFile.seekSet(orderListOffset + songLength);
+
+	// Read pattern offsets.
+	for (byte i = 0; i < 32; i++) {
+		patternOffsets[i] = radFile.read() + (radFile.read() << 8);
+	}
+}


Update for changes to PID library

Update for changes to PID library

--- inverted-pendulum.ino
+++ inverted-pendulum.ino
@@ -32,7 +32,7 @@
 Pendulum pendulum(1200, 100.0);

 // PID control for cart position
-float kp = 2, ki = 200, kd = 0; // Initial/default PID gain coeffs
+float kp = 2, ki = 0.2, kd = 0; // Initial/default PID gain coeffs
 PIDControl cartPid(kp, ki, kd, 0, 10); // p,i,d, initial setpoint, timestep [ms]
 elapsedMillis printTimer = 0;
 unsigned int printerval = 50; // ms
@@ -265,7 +265,7 @@
   {
     cartPid.setpoint = pendulum.swingX(0.5);
   }
-  cartPid.update(pendulum.x);
+  cartPid.update(pendulum.x, millis());

   // Report rise time
   if (riseTimerStarted && deltaSetpoint != 0)


Kapitola: Prekážky

Kapitola: Prekážky


green LED on iTag board after init

green LED on iTag board after init

--- Reaper/examples/CommandLoop/CommandLoop.ino
+++ Reaper/examples/CommandLoop/CommandLoop.ino
@@ -4,17 +4,17 @@

 #include <ReaperCommandProcessor.h>

-const int chipSelect = 10;
+const int chipSelect = SS;

 Reaper reaper = Reaper(SerialUSB, chipSelect);
 ReaperCommandProcessor proc(SerialUSB, reaper);

 void setup()
 {
-  pinMode(13, OUTPUT);
-  digitalWrite(13, LOW);
+  pinMode(12, OUTPUT);
+  digitalWrite(12, LOW);

-  SerialUSB.begin(9600);
+  SerialUSB.begin(9600, SERIAL_8N1);
   Serial.begin(9600, SERIAL_8N1);
   while (!SerialUSB);

@@ -22,7 +22,7 @@
   reaper.init();
   SerialUSB.println("OK");
   Serial.println("Ready");
-  digitalWrite(13, LOW);
+  digitalWrite(12, HIGH);
 }

 void loop(void) {


TFF Edits 9.19

TFF Edits 9.19

--- SBR/SBR.ino
+++ SBR/SBR.ino
@@ -455,23 +455,24 @@
    //aeration(60,17);
    //settle(50, 19);//delay(1000);
    //decant(6);//loading();
+    normalsequence();
     x++;}

     //this section checks what day it is then starts the sequence if time of day is between these hours
   if (now.dayOfTheWeek()== 1){//"Monday"
-    if (now.hour()>4 && now.hour()<6){normalsequence();}}
+    if (now.hour()>5 && now.hour()<7){normalsequence();}}
   if (now.dayOfTheWeek()== 2){//"Tuesday"
-    if (now.hour()>4 && now.hour()<6){normalsequence();}}
+    if (now.hour()>5 && now.hour()<7){normalsequence();}}
   if (now.dayOfTheWeek()== 3){//"Wednesday"
-    if (now.hour()>4 && now.hour()<6){normalsequence();}}
+    if (now.hour()>5 && now.hour()<7){normalsequence();}}
   if (now.dayOfTheWeek()== 4){//"thursday"
-    if (now.hour()>4 && now.hour()<6){normalsequence();}}
+    if (now.hour()>5 && now.hour()<7){normalsequence();}}
   if (now.dayOfTheWeek()== 5){//"Friday"
-    if (now.hour()>4 && now.hour()<6){normalsequence();}}
+    if (now.hour()>5 && now.hour()<7){normalsequence();}}
   if (now.dayOfTheWeek()== 6){//"Saturday"
-    if (now.hour()>4 && now.hour()<6){normalsequence();}}//fillplusair(21,1);thirtysecaeration(100,10);}}
+    if (now.hour()>5 && now.hour()<7){normalsequence();}}//fillplusair(21,1);thirtysecaeration(100,10);}}
   if (now.dayOfTheWeek()== 0){//"Sunday"
-    if (now.hour()>4 && now.hour()<6){normalsequence();}}//weekend();}
+    if (now.hour()>5 && now.hour()<7){normalsequence();}}//weekend();}
   delay(20000);
   ////This weekend function is poorly named but means what happens before the sequence is triggered and after it is done
   weekend();}


apollo: fast return from keyb polling

apollo: fast return from keyb polling


disable bod

disable bod

--- ScoutWeatherStation/scout_weather_station.ino
+++ ScoutWeatherStation/scout_weather_station.ino
@@ -414,6 +414,8 @@
   Serial.println("going to deep sleep");
   delay(100); // let serial write
   sleep_mode();
+  sleep_bod_disable();  // Additionally disable the Brown out detector
+

   /* The program will continue from here after the WDT timeout*/
   sleep_disable(); /* First thing to do is disable sleep. */


Added interval check

Added interval check

--- securitylab/securitylab.ino
+++ securitylab/securitylab.ino
@@ -43,8 +43,12 @@
 const char *host = "arjan-schouten.nl";
 const int port = 4443;

+const long pingInterval = 5000;
+
 //Create a http handler for connection with the server
 Http http = Http(host, port);
+
+long previousPingMillis;

 //Setup the server with the
 ESP8266WebServer server(80);
@@ -80,11 +84,26 @@
   // set master brightness control
   FastLED.setBrightness(BRIGHTNESS);

+  previousPingMillis = millis();
 }

 void loop() {
   // update the AnalogMultiButton object every loop
   buttons.update();
+
+  // Ping the server on a specified interval
+  if (millis() > previousPingMillis + pingInterval) {
+    Serial.println("Sending a ping request...");
+    Http::PingResult result;
+    http.pingServer(deviceId, version, &result);
+
+    if (result.update) {
+      t_httpUpdate_return ret = ESPhttpUpdate.update(host, 8080, "/update/" + String(result.newversion));
+      Serial.println(ESPhttpUpdate.getLastErrorString());
+    }
+
+    previousPingMillis = millis();
+  }

   //  Debugging buttons
   //  Serial.println(analogRead(A0));
@@ -123,38 +142,19 @@

   if (buttons.onRelease(BUTTON_SEND))
   {
-    Serial .println("Message is Recorded, Send message");
+    Serial.println("Message is Recorded, Send message");
     for (int i = 0; i < NUM_LEDS; i++) {
       leds[i] = CRGB::Green;
       FastLED.show();
     }

-    delay(2000);
+    http.sendMessage(deviceId, "Hoi!!!");

     for (int k = 0; k < NUM_LEDS; k++) {
       leds[k] = CRGB::Black;
       FastLED.show();
     }
   }
-
-  Http::PingResult result;
-  http.pingServer(deviceId, version, &result);
-#ifdef DEBUG
-  Serial.print("Message: ");
-  Serial.println(result.message);
-
-  Serial.print("Update: ");
-  Serial.println(result.update ? "Yes" : "No" );
-  Serial.print("New version: ");
-  Serial.println(result.newversion);
-#endif
-
-  if (result.update) {
-    t_httpUpdate_return ret = ESPhttpUpdate.update(host, 8080, "/update/" + String(result.newversion));
-    Serial.println(ESPhttpUpdate.getLastErrorString());
-  }
-
-  http.sendMessage(deviceId, "Hoi!!!");

   processWiFi();
 #ifdef DEBUG


Delete airsolarcontroller.ino

Delete airsolarcontroller.ino

--- airsolarcontroller/airsolarcontroller.ino
+++ airsolarcontroller/airsolarcontroller.ino
@@ -1,874 +0,0 @@
-/*
-   Arduino Solar Controller - ASC-Kit v1.3
-
-   by karldm, Dec 2016
-
-
-   The MIT License (MIT)
-
-   Copyright (c) 2016 www.renergia.fr
-
-   Permission is hereby granted, free of charge, to any person obtaining a copy
-   of this software and associated documentation files (the "Software"), to deal
-   in the Software without restriction, including without limitation the rights
-   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-   copies of the Software, and to permit persons to whom the Software is
-   furnished to do so, subject to the following conditions:
-
-   The above copyright notice and this permission notice shall be included in all
-   copies or substantial portions of the Software.
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-   SOFTWARE.
-*/
-
-//-------1---------2---------3---------4---------5---------6---------7---------8
-#define VERSION "asc.0.0i"        // Software version
-#define TAG10   "ASCKit...."      // Tag for EEPROM data structure check -- unused
-
-//
-// SOLAR HEATER STATES
-// modes  : OFF - HEAT
-// states : OFF - ON
-//
-// MAIN HEATER
-// modes  : OFF - HEAT - ECO - FP
-// states : ON - OFF
-
-#define OFF     0                  // OFF mode or state
-#define ON      1                  // ON state
-#define HEAT    1                  // main heater & solar heater HEAT mode
-#define ECO     2                  // Main heater ECO mode
-#define FP      3                  // Main heater freeze protection
-
-//
-// CONTROLLER
-// states : STOP - RUN - INIT - FAIL1 - FAIL2
-//
-#define STOP      0                // Controller STOP state or mode
-#define RUN       1                // Controller RUN state or mode
-#define INIT      2                // Controller INIT state -- power onP
-#define FAIL1     3                // Controller FAIL1 -- solar heater OFF due to TCOL measurement error
-#define FAIL2     4                // Controller FAIL2 state -- sh and mh OFF due to TAMB measurements error
-
-// Hardware configuration
-#define PINDHT    2                // DHT sensor bus
-#define PINLED1   3                // White LED status pin number
-#define PINOW1    4                // OW1 Bus pin number (single sensor TCOL)
-#define PINOW2    5                // OW2 Bus pin number (single sensor TEXT)
-#define PINOW3    6                // OW1 Bus pin number (single sensor TUSR1)
-#define PINOW4    7                // OW2 Bus pin number (single sensor TUSR2)
-#define PINSWMH   8                // SWMH pin number
-#define PINSWSH   9                // SWSH pin number
-#define PINSWUSR 12                // SWUSR pin number
-
-// sensors
-// masks for the ERRSENSOR parameter (use for error diagnostic)
-#define MASKTAMB   0b00000001
-#define MASKHAMB   0b00000010
-#define MASKTCOL   0b00000100
-#define MASKTEXT   0b00001000
-#define MASKTUSR1  0b00010000
-#define MASKTUSR2  0b00100000
-
-#define TEMPERATURE_RESOLUTION 12  // nb bits for DS18B20 sensors
-
-//=== DHT SENSOR MODEL ===
-#define DHTTYPE DHT22            // DHT 22  == AM2302 == AM2321
-
-// includes
-#include <avr/pgmspace.h>
-
-#include <Console.h>
-#include <Bridge.h>
-
-#include <DHT.h>
-#include <OneWire.h>
-#include <DallasTemperature.h>
-
-#include "ascutil.h"
-#include "ascdata.h"
-
-//-------1---------2---------3---------4---------5---------6---------7---------8
-// Ascdata objet - global access -- needed?
-Ascdata ascdata;
-
-/************************************************************/
-/***    PARAMETERS TYPE DECLARATION AND DEFAULT VALUES    ***/
-/************************************************************/
-
-// available types are :
-//   byte
-//   int            => TEMP
-//   unsigned long  => ULONG
-
-/*
- * Sensors
- */
-TEMP   TAMB;               // ambiant temperature (.01 degC)
-int    HAMB;               // humidity (0.01%)
-TEMP   TCOL;               // collector temperature (.01 degC)
-TEMP   TEXT;               // external temperature (.01 degC)
-TEMP   TUSR1;              // temperature user#1 (.01 degC)
-TEMP   TUSR2;              // temperature user#2 (.01 degC)
-
-/*
- * Calculated
- */
-int    NLOOPS = 0;         // number of loops per sec
-int    PSOLTH = 0;         // heating solar power (W)
-byte   SWSH = OFF;         // Solar heater switch (FAN)
-byte   SWMH = OFF;         // Main heater switch
-byte   SWUSR = OFF;        // user switch
-byte   STATESH = OFF;      // begin with the OFF state
-byte   STATEMH = OFF;      // begin with OFF state
-byte   STATECTRL = INIT;   // controller INIT state at power on
-byte   ERRSENSOR = 0;      // error status on sensor -- begin with no error
-byte   ERRCTRL = 0;        // internal error id# -- go to STOP mode if != 0
-ULONG  INDSH = 0;          // Cumulative index of solar energy (Wh)
-ULONG  TCSH = 0;           // Cumulated time of SH usage (h)
-ULONG  TCMH = 0;           // Cumulated time of MH usage (h)
-
-/*
- * Controller parameters
- */
-TEMP   TSET = 2400;        // set point (.01 degC)
-TEMP   DTECO = 500;        // delta T eco (.01 degC)
-byte   MODESH = ON;        // mode solar heater
-byte   MODEMH = ON;        // mode main heater
-ULONG  TMHON = 120;        // min time main heater in ON state(s)
-ULONG  TMHOFF = 120;       // min time main heater in OFF state(s)
-ULONG  TSHON = 120;        // min time Fan in ON state (s)
-ULONG  TSHOFF = 120;       // min time Fan in OFF state (s)
-TEMP   DTSHON = 600;       // delta T Fan ON (.01 deg.C)
-TEMP   DTSHOFF = 300;      // delta T Fan OFF (.01 deg.C)
-TEMP   AUG1 = 200;         // Solar heater set point delta (.01 deg.C)
-TEMP   AUG2 =   0;         // Main heater set point delta (.01 deg.C)
-TEMP   TFP = 800;          // Freeze protection set point (.01 deg.C)
-TEMP   HYST = 100;         // Hysteresis parameter (.01 deg.C)
-TEMP   DTDHT = 0;          // Overheating of the ambiance sensor when mounted inside the DIN package -- 0 if external mounting
-                           // 1000 for closed mounting
-int    VFAN = 200;         // Fan flow (cub.m/h)
-int    ASOLT = 4;          // Thermal collectors total area (sq.m) -- for INDSH calculation
-byte   SYSTEM = 1;         // System type (1=MH only, 2=MH+SH)
-byte   CONF1 = 1;          // Air circulation configuration (1=External, 2=Recirculated) -- for INDSH calculation
-
-/************************************************************/
-/********** END PARAMETERS DEFAULT VALUES *******************/
-/************************************************************/
-
-// Define the x.hour counters
-Counterh counterhTCMH( TCMH );   // time usage main heater (h)
-Counterh counterhTCSH( TCSH );   // time usage solar heater (h)
-Counterh counterhINDSH( INDSH ); // estimated usefull solar energy (Wh)
-
-// Various timers - see usage in util.cpp
-// declare delays in millis
-Timer  timerOW( 750 / (1 << (12 - TEMPERATURE_RESOLUTION)) ); // min delay for DS18B20 convertion
-Timer  timerDHT( 2000 );          // DHT readout delay, may be reduced...
-Timer  timerMH( 0 );              // main heater time-in-state, begin with OFF state
-Timer  timerSH( 0 );              // solar heater time-in-state, begin with OFF state
-Timer  timerEEPROM( ONEHOURMS );  // update period of EEPROM data
-Timer  timerLED( 3456 );          // flash the LED1
-Timer  timerBridge( 2000 );       // synchronize bridge data (i.e. commands responsivity...)
-
-// DHT sensor bus
-DHT dht(PINDHT, DHTTYPE);
-
-// 1-Wire buses & sensors
-OneWire ow1(PINOW1);    // for TCOL
-OneWire ow2(PINOW2);    // for TAMB
-OneWire ow3(PINOW3);    // for TUSR1
-OneWire ow4(PINOW4);    // for TUSR2
-
-DallasTemperature sensor1(&ow1);
-DallasTemperature sensor2(&ow2);
-DallasTemperature sensor3(&ow3);
-DallasTemperature sensor4(&ow4);
-
-/*
- * === setup() ===
- */
-void setup() {
-  // Set PinMode
-  pinMode( PINLED1, OUTPUT );
-  pinMode( PINDHT, OUTPUT );
-  pinMode( PINOW1, OUTPUT );
-  pinMode( PINOW2, OUTPUT );
-  pinMode( PINSWMH, OUTPUT );
-  pinMode( PINSWSH, OUTPUT );
-  pinMode( PINSWUSR, OUTPUT );
-
-  // setoutputs -- check STATECTRL = INIT
-  // done azap -- set switches in OFF position
-  STATECTRL = INIT; // force the INIT state
-  StateEngine();    // calculate the heaters states
-  SetOutputs();     // set the outputs
-
-  // bridge
-  Bridge.begin();   // start the script /usr/bin/run-bridge on the MPU side
-
-  // infos to the console by PrintInfo()
-  // only for debug purpose! It's waiting for the console for ever
-  BeginInfo();      // start the Console if CONSOLE if defined in util.h -- dev
-  PrintInfo('i', "Arduino Solar Controller - Kit, v%s", VERSION);
-
-  /************************************************************
-     WARNING:
-     THE DECLARATION ORDER DEFINES THE DATA STORAGE IN EEPROM
-     ANY CHANGE WILL PREVENT TO RETRIEVE THE STORED DATAS
-   ************************************************************/
-  //
-  // Parameters declaration
-  // ( pointer, label, options)
-  // options = "<access> <format>" (! only one space)
-  //   where <access> = {pgs}
-  //                  p: put the value to datastore
-  //                  g: get the value from datastore
-  //                  s: save the value into the EEPROM
-  //         <format> = f4.2 (xxxx.xx) i -- ipv4 (x.x.x.x)
-  //
-
-  // usr : user parameters
-  ascdata.par_F( &TSET,     F("tset"),     F("gs f4.2"));
-  ascdata.par_F( &DTECO,    F("dteco"),    F("gs f4.2"));
-  ascdata.par_F( &MODESH,   F("modesh"),   F("gs i"));
-  ascdata.par_F( &MODEMH,   F("modemh"),   F("gs i"));
-
-  // dev : device parameters
-  ascdata.par_F( &TMHON,    F("tmhon"),    F("gs i"));
-  ascdata.par_F( &TMHOFF,   F("tmhoff"),   F("gs i"));
-  ascdata.par_F( &TSHON,    F("tshon"),    F("gs i"));
-  ascdata.par_F( &TSHOFF,   F("tshoff"),   F("gs i"));
-  ascdata.par_F( &DTSHON,   F("dtshon"),   F("gs f4.2"));
-  ascdata.par_F( &DTSHOFF,  F("dtshoff"),  F("gs f4.2"));
-  ascdata.par_F( &AUG1,     F("aug1"),     F("gs f4.2"));
-  ascdata.par_F( &AUG2,     F("aug2"),     F("gs f4.2"));
-  ascdata.par_F( &TFP,      F("tfp"),      F("gs f4.2"));
-  ascdata.par_F( &HYST,     F("hyst"),     F("gs f4.2"));
-  ascdata.par_F( &DTDHT,    F("dtdht"),    F("gs f4.2"));
-  ascdata.par_F( &VFAN,     F("vfan"),     F("gs i"));
-  ascdata.par_F( &ASOLT,    F("asolt"),    F("gs i"));
-  ascdata.par_F( &SYSTEM,   F("system"),   F("gs i"));
-  ascdata.par_F( &CONF1,    F("conf1"),    F("gs i"));
-
-  // Calculated
-  ascdata.par_F( &NLOOPS,   F("nloops"),   F("p i"));
-  ascdata.par_F( &PSOLTH,   F("psolth"),   F("p i"));
-  ascdata.par_F( &INDSH,    F("indsh"),    F("ps i"));     // update period tbd
-  ascdata.par_F( &TCMH,     F("tcmh"),     F("ps i"));     // update period tbd
-  ascdata.par_F( &TCSH,     F("tcsh"),     F("ps i"));     // update period tbd
-
-  // sensors & switches
-  ascdata.par_F( &TAMB,     F("tamb"),     F("p f4.2"));
-  ascdata.par_F( &HAMB,     F("hamb"),     F("p f4.2"));
-  ascdata.par_F( &TCOL,     F("tcol"),     F("p f4.2"));
-  ascdata.par_F( &TEXT,     F("text"),     F("p f4.2"));
-  ascdata.par_F( &TUSR1,    F("tusr1"),    F("p f4.2"));
-  ascdata.par_F( &TUSR2,    F("tusr2"),    F("p f4.2"));
-  ascdata.par_F( &SWSH,     F("swsh"),     F("p i"));
-  ascdata.par_F( &SWMH,     F("swmh"),     F("p i"));
-  ascdata.par_F( &SWUSR,    F("swusr"),    F("gs i"));     // user switch -- saved?
-
-  // states
-  ascdata.par_F( &STATEMH,  F("statemh"),  F("p i"));
-  ascdata.par_F( &STATESH,  F("statesh"),  F("p i"));
-  ascdata.par_F( &STATECTRL, F("statectrl"), F("p i"));
-
-  // errors
-  ascdata.par_F( &ERRSENSOR, F("errsensor"), F("p i"));
-  ascdata.par_F( &ERRCTRL,   F("errctrl"),   F("p i"));
-
-  /************************************************************/
-  /***************  END OF DECLARATION   **********************/
-  /************************************************************/
-  ascdata.getNpar(); // info on data usage if console activated -- dev
-
-  //
-  // Get configuration from EEPROM (if available and consistent with the present version (TAG10)
-  //
-  PrintInfo( 'i', F("Get EEPROM saves data..."));
-
-  if ( ascdata.EEPROM_get( TAG10, 0 ) != 0 ) {
-    // data structure not compatible
-    // Note: EEPROM is reset after a schetch upload => we write the default values
-    PrintInfo( 'w', F("EEPROM TAG error."));
-    // then update the EEPROM with the defaults values defined in the sketch
-    // counters values are reset
-    PrintInfo( 'i', F("Update EEPROM data with the sketch default values..."));
-    ascdata.EEPROM_put( TAG10, 0 );
-    // write the default values into EEPROM as well
-    ascdata.EEPROM_put( TAG10, 1 );
-  }
-  else {
-    // Data retrieved from the EEPROM are ok
-    // Update counters values
-    counterhTCMH.set( TCMH );
-    counterhTCSH.set( TCSH );
-    counterhINDSH.set( INDSH );
-  }
-
-  //
-  // 1-Wire Initialization
-  //
-  // Start up the library and define the resolution
-  // Define the Async mode
-  // Start a conversion
-  //
-  OWsensorBegin( sensor1 );
-  OWsensorBegin( sensor2 );
-  OWsensorBegin( sensor3 );
-  OWsensorBegin( sensor4 );
-
-  // Keys generation in datastore
-  //
-  // put all the data to create the keys
-  ascdata.bridgePut('*'); // create all the keys in datastore
-
-  // add the 'version' data
-  ascdata.bridgePutVersion( VERSION );
-
-  // add the 'request' data
-  ascdata.bridgePutRequest("none");
-
-  // start the controller
-  LedBlinkingN( PINLED1, 200, 4 ); // indicate that the INIT phase is finished
-  STATECTRL = RUN; // go into RUN state after power on
-
-  // setup() finished
-  PrintInfo( 'i', F("Starting loop()..."));
-}
-
-/*
- * === loop() ===
- */
-void loop() {
-  //
-  static int nloops = 0;
-  static Timer timerLoops( 1000 );
-  int index;
-
-  ReadSensors();
-  CalcParameters();
-  StateEngine();
-  SetOutputs();
-
-  // loop performance calculation
-  nloops++;
-  if ( timerLoops.check() ) {
-    NLOOPS = nloops; // nb loops per sec
-    nloops = 0;
-  }
-
-  if ( timerBridge.check() ) {
-    // Bridge synchronization
-    // do it only at low rate (~500ms), but may be optimized
-    //
-    // Retrieve data from datastore (bridge)
-    // We get only the 'g' access values
-    if ( ascdata.bridgeGet('g') != 0 ) {
-      ascdata.EEPROM_put( TAG10, 0 ); // update EEPROM data if some modifications in saved data
-      PrintInfo( 'i', F("EEPROM update due to datastore change"));
-    }
-
-    // Update the data into datastore (bridge)
-    // we only put the 'p' access values
-    // you should not have both 'p' and 'g' access for the same data...
-    ascdata.bridgePut('p');
-
-    // request handle
-    // see RequestHandle()
-    if (ascdata.bridgeGetRequest()) {
-      ascdata.bridgePutRequest("none"); // reset request into datastore
-      RequestHandle();                  // execute the request
-    }
-  }
-
-  if ( timerEEPROM.check() ) {
-    ascdata.EEPROM_put( TAG10, 0 ); // periodic update of EEPROM saved data (counters)
-    PrintInfo( 'i', F("timerEEPROM update."));
-  }
-}
-
-/*
- * === ReadSensors() ===
- */
-void ReadSensors() {
-  // read the sensors
-  // what if an error occur?
-  // no check for time serie (i.e. glitches)
-  float dhtval;
-
-  /////////////////////
-  // Read DHT sensor //
-  // TAMB & HAMB     //
-  /////////////////////
-  if ( timerDHT.check() ) {
-
-    dhtval = dht.readTemperature(); // default is Celsius
-    if (isnan(dhtval)) {
-      // FAIL
-      if ( IsSensorValid( MASKTAMB ) ) PrintInfo( 'w', F("Failed to read from DHT sensor TAMB!...")); // only once
-      ErrSensorRaise( MASKTAMB );
-    }
-    else {
-      if ( !IsSensorValid( MASKTAMB ) ) PrintInfo( 'i', F("DHT sensor TAMB ok...")); // only once
-      TAMB = int(100 * dhtval) - DTDHT; // correction for overheating
-      ErrSensorClear( MASKTAMB );
-    }
-
-    dhtval = dht.readHumidity();
-    if (isnan(dhtval)) {
-      // soft FAIL
-      if ( IsSensorValid( MASKHAMB ) ) PrintInfo( 'w', F("Failed to read from DHT sensor HAMB!...")); // only once
-      ErrSensorRaise( MASKHAMB );
-    }
-    else {
-      if ( !IsSensorValid( MASKHAMB ) ) PrintInfo( 'i', F("DHT sensor HAMB ok...")); // only once
-      HAMB = int(100 * dhtval);
-      //simple correction for humidity
-      HAMB = HAMB + 100*int(DTDHT/(2*(100-int(HAMB/100))/3+6));
-      ErrSensorClear( MASKHAMB );
-    }
-  }
-
-  ///////////////////////////
-  // Read OW1 Bus => TCOL  //
-  // Read OW2 Bus => TEXT  //
-  // Read OW3 Bus => TUSR1 //
-  // Read OW4 Bus => TUSR2 //
-  ///////////////////////////
-  if ( timerOW.check() ) {
-    //
-    // to do -- should add error messages
-    //
-    TCOL  = ReadOWbus( sensor1, MASKTCOL );
-    TEXT  = ReadOWbus( sensor2, MASKTEXT );
-    TUSR1 = ReadOWbus( sensor3, MASKTUSR1 );
-    TUSR2 = ReadOWbus( sensor4, MASKTUSR2 );
-  }
-}
-
-/*
- * === CalcParameters() ===
- */
-void CalcParameters() {
-  // Parameters calculation
-  PSOLTH = 0; // default value
-  if ( STATESH == ON ) {
-    // PSOLTH (W) -- check positive value
-    if ( CONF1 == 1 && TCOL > TEXT ) PSOLTH = int( 0.335 * VFAN * (TCOL - TEXT) / 100 ); // external air
-    if ( CONF1 == 2 && TCOL > TAMB ) PSOLTH = int( 0.335 * VFAN * (TCOL - TAMB) / 100 ); // recirculated
-  }
-
-  // counters calls for cumulative quantities
-  TCMH = int(counterhTCMH.add(1)); // time integration (h)
-  TCSH = int(counterhTCSH.add(1)); // time integration (h)
-  INDSH = int(counterhINDSH.add((unsigned long) PSOLTH)); // power integration (Wh)
-}
-
-/*
- * === StateEngine() ===
- */
-void StateEngine() {
-  //
-  // Define the controller state STATECTRL ( INTI - STOP - RUN - FAIL1 - FAIL2 ) -- INIT defined during setup()
-  // Define the heaters states: STATEMH & STATESH
-  // See SetOutputs()
-  //
-  // TO BE CHECKED -- correction FAIL2???
-  //
-  if ( STATECTRL == INIT ) {
-    STATEMH = OFF;
-    STATESH = OFF;
-  }
-  else if ( STATECTRL == STOP ) {
-    STATEMH = OFF;
-    STATESH = OFF;
-  }
-  else { // i.e. RUN, FAIL1 or FAIL2
-    if ( IsSensorValid(MASKTAMB)&& IsSensorValid(MASKTCOL) ) {
-      STATECTRL = RUN;
-      StateEngineMH();
-      StateEngineSH();
-    }
-    else if ( IsSensorValid(MASKTAMB)&& !IsSensorValid(MASKTCOL) ) {
-      // patch for single zone usage
-      if ( SYSTEM == 2 ) {
-        STATECTRL = FAIL1;
-      }
-      else {
-        STATECTRL = RUN; // ignore TCOL error
-      }
-      StateEngineMH();
-      STATESH = OFF;
-    }
-    else {
-      STATECTRL = FAIL2; // ok?
-      STATEMH = OFF;
-      STATESH = OFF;
-    }
-  }
-
-  if ( ERRCTRL != 0 ) { // catch internal error => STOP
-    STATECTRL = STOP;
-    STATEMH = OFF;
-    STATESH = OFF;
-    // the only way to quit this mode is to reset the board or to send /data/put/request/run
-  }
-}
-
-/*
- * === StateEngineMH() ===
- */
-void StateEngineMH() {
-  // Main heater
-  boolean c1, c2, c3, c4;     // conditions
-  TEMP tsetmh;                // internal setpoint -- depends on MODEMH
-
-  // define tsetmh -- depends on MODEMH
-  switch ( MODEMH ) {
-    case HEAT:
-      tsetmh = TSET;
-      break;
-
-    case ECO:
-      tsetmh = TSET - DTECO;
-      break;
-
-    case FP:
-      tsetmh = TFP;
-      break;
-
-    default:
-      tsetmh = TSET;
-  }
-
-  switch ( STATEMH ) {
-    case OFF:
-      // compute the conditions to go ON
-      c1 = (MODEMH != OFF);
-      c2 = timerMH.check( 1000*TMHOFF );               // is minimum time in the OFF state?
-      c3 = (TAMB < tsetmh) && (STATESH == OFF);          // condition if STATESH = OFF
-      c4 = (TAMB < tsetmh - AUG2) && (STATESH == ON);    // condition if STATESH = ON
-
-      if ( c1 && c2 && (c3 || c4) ) {
-        PrintInfo('i', F("Main heater switched to ON"));
-        STATEMH = ON;
-        timerMH.start();                               // restart timer
-        counterhTCMH.run();                            // run time counter
-      }
-      break;
-
-    case ON:
-      // compute the conditions to go OFF
-      c1 = (MODEMH == OFF);
-      c2 = timerMH.check( 1000*TMHON );                      // is minimum time in the ON state?
-      c3 = (TAMB > tsetmh + HYST) && (STATESH == OFF);       // condition if STATESH = OFF
-      c4 = (TAMB > tsetmh + HYST - AUG2) && (STATESH == ON); // condition if STATESH = ON
-
-      if ( c1 || (c2 && (c3 || c4)) ) {
-        PrintInfo('i', F("Main heater switched to OFF"));
-        STATEMH = OFF;
-        timerMH.start();                              // restart timer
-        counterhTCMH.stop();                          // stop time counter
-      }
-      break;
-
-    default:
-      // error in software
-      ERRCTRL = 91; // at least... call the vendor! -- ERRCTRL = ???
-      PrintInfo('w', F("Internal error ERRCTRL=91"));
-  }
-}
-/*
- * ===stateEngineSH() ===
- */
-void StateEngineSH() {
-  // Solar heater
-  boolean c1, c2, c3, c4;     // conditions
-
-  switch ( STATESH ) {
-    case OFF:
-      // compute the conditions to go ON
-      c1 = (MODESH == ON);
-      c2 = timerSH.check( 1000*TSHOFF );               // is minimum time in the OFF state?
-      c3 = (TCOL > TAMB + DTSHON);                     // solar collector temp condition
-      c4 = (TAMB < TSET + AUG1);                       // ambiant condition
-
-      if ( c1 && c2 && c3 && c4 ) {
-        PrintInfo('i', F("Solar heater switched to HEAT"));
-        STATESH = ON;
-        timerSH.start();                               // restart timer
-        counterhINDSH.run();                           // run energy counter
-        counterhTCSH.run();                            // run time counter
-      }
-      break;
-
-    case ON:
-      // compute the conditions to go OFF
-      c1 = (MODESH == OFF);
-      c2 = timerSH.check( 1000*TSHON );                // is minimum time in the ON state?
-      c3 = (TCOL < TAMB + DTSHOFF);                    // solar collector temp condition
-      c4 = (TAMB > TSET + HYST + AUG1);                // ambiant condition
-
-      if ( c1 || (c2 && (c3 || c4)) ) {
-        PrintInfo('i', F("Solar heater switched to OFF"));
-        STATESH = OFF;
-        timerSH.start();                               // restart timer
-        counterhINDSH.stop();                          // stop energy counter
-        counterhTCSH.stop();                           // stop time counter
-      }
-      break;
-
-    default:
-      // error in software
-      ERRCTRL = 92; // at least... call the vendor!
-      PrintInfo('w', F("Internal error ERRCTRL=92"));
-  }
-}
-
-/*
- * SetOutputs()
- */
-void SetOutputs() {
-  //
-  // Set the outputs regarding to the controller state
-  // the controller state is defined in StateEngine()
-  //
-  // Set the heaters switches -- defined by STATEMH and STATESH
-
-  // main heater
-  if (STATEMH == ON) {
-    SWMH = ON;
-    digitalWrite( PINSWMH, HIGH );
-  }
-  else {
-    SWMH = OFF;
-    digitalWrite( PINSWMH, LOW );
-  }
-
-  // solar heater
-  if (STATESH == ON) {
-    SWSH = ON;
-    digitalWrite( PINSWSH, HIGH );
-  }
-  else {
-    SWSH = OFF;
-    digitalWrite( PINSWSH, LOW );
-  }
-
-  // switch user -- no error catch, simply copy the SWUSR value
-  if (SWUSR == HIGH) {
-    digitalWrite( PINSWUSR, HIGH );
-  }
-  else {
-    digitalWrite( PINSWUSR, LOW );
-  }
-  //
-  // set the LED1
-  switch ( STATECTRL ) {
-
-    case INIT:
-      // show LED1
-      LedBlinkingN( PINLED1, 100, 4 ); // 4 short pulses
-      digitalWrite( PINLED1, HIGH );   // lighted during INIT phase
-      break;
-
-    case STOP:
-      LedBlinkingN( PINLED1, 100, 5 ); // 5 pulses
-      break;
-
-    case RUN:
-      // run phase: switches controlled by the states
-      //LedBlinking( PINLED1, 100, &timerLED ); // RUN => short flash
-      LedGlowing( PINLED1, 6740, 20, 100 );
-      break;
-
-    case FAIL1:
-      LedBlinkingN( PINLED1, 100, 1 ); // one pulse
-      break;
-
-    case FAIL2:
-      LedBlinkingN( PINLED1, 100, 2 ); // two pulses
-      break;
-  }
-}
-
-/*
- *  === RequestHandle() ===
- */
-void RequestHandle() {
-  //
-  // Requests from datastore: data/put/request/request_type
-  //
-  // stop:          set STATECTRL to STOP
-  //+run:           set STATECTRL to RUN
-  // default:       retrieve the EEPROM values
-  //+setdefault:    copy the current values into the EEPROM (default values)
-  // rst_indsh:     reset the solar ventilation energy counter
-  // rst_tcsh:      reset the solar ventilation time counter
-  // rst_tcsh:      reset the main heater time counter
-  //
-
-  if ( ascdata.isRequest("stop") ) {
-    PrintInfo( 'i', F("request = stop"));
-    STATECTRL = STOP;
-  }
-
-  else if ( ascdata.isRequest("run") ) {
-    PrintInfo( 'i', F("request = run"));
-    STATECTRL = RUN;
-  }
-
-  else if ( ascdata.isRequest("default") ) {
-    PrintInfo( 'i', F("request = default"));
-
-    // get default data from EEPROM
-    // no need to check the TAG
-    ascdata.EEPROM_get( TAG10, 1 );
-    // update saved values
-     ascdata.EEPROM_put( TAG10, 0 );
-
-    // Update current counters values -- reset!
-    counterhTCMH.set( TCMH );
-    counterhTCSH.set( TCSH );
-    counterhINDSH.set( INDSH );
-    // update datastore -- otherwise, current datastore data will be retrieved!
-    ascdata.bridgePut('s'); // only the saved data are modified
-  }
-
-  else if ( ascdata.isRequest("setdefault") ) {
-    PrintInfo( 'i', F("request = setdefault"));
-    PrintInfo( 'i', F("EEPROM default data update with current values."));
-
-    // save current data into EEPROM default data
-    ascdata.EEPROM_put( TAG10, 1 );
-  }
-
-  else if ( ascdata.isRequest("rst_indsh") ) {
-    PrintInfo( 'i', F("request = rst_indsh"));
-
-    INDSH = 0;
-    counterhINDSH.set( INDSH );  // set the counter value
-
-    // save the values
-    PrintInfo( 'i', F("EEPROM update with current values."));
-    // update EEPROM data with current values
-    ascdata.EEPROM_put( TAG10, 0 );
-
-    // update datastore -- to be sure!
-    ascdata.bridgePut('s'); // only the saved data are modified
-  }
-
-  else if ( ascdata.isRequest("rst_tcmh") ) {
-    PrintInfo( 'i', F("request = rst_tcmh"));
-
-    TCMH = 0;
-    counterhTCMH.set( TCMH );  // set the counter value
-
-    // save the values
-    PrintInfo( 'i', F("EEPROM update with current values."));
-    // update EEPROM data with current values
-    ascdata.EEPROM_put( TAG10, 0 );
-
-    // update datastore -- to be sure!
-    ascdata.bridgePut('s'); // only the saved data are modified
-  }
-
-  else if ( ascdata.isRequest("rst_tcsh") ) {
-    PrintInfo( 'i', F("request = rst_tcsh"));
-
-    TCSH = 0;
-    counterhTCSH.set( TCSH );  // set the counter value
-
-    // save the values
-    PrintInfo( 'i', F("EEPROM update with current values."));
-    // update EEPROM data with current values
-    ascdata.EEPROM_put( TAG10, 0 );
-
-    // update datastore -- to be sure!
-    ascdata.bridgePut('s'); // only the saved data are modified
-  }
-
-  else {
-    PrintInfo('w', F("Undefined request"));
-  }
-
-/*
- * === end request handle ===
- */
-}
-
-// #############################
-// utilities for ERRSENSOR usage
-// #############################
-
-/*
- * IsSensorValid()
- */
-bool IsSensorValid( byte mask ) {
-  // Return true if the sensor is ok
-  return( (ERRSENSOR & mask) == 0 );
-}
-
-/*
- * ErrSensorRaise()
- */
-void ErrSensorRaise( byte mask ) {
-  // Raise an error on sensor
-  ERRSENSOR = ERRSENSOR | mask;
-}
-
-/*
- * ErrSensorClear()
- */
-void ErrSensorClear( byte mask ) {
-  // Clear error on sensor
-  ERRSENSOR = ERRSENSOR & ~mask;
-}
-
-// ################
-// 1-Wire utilities
-// ################
-
-/*
- * OWsensorBegin()
- */
-void OWsensorBegin(DallasTemperature sensor) {
-  // OW begin
-  // variable to get the device address of a 1-wire sensor
-  DeviceAddress deviceAddress;
-  //
-  sensor.begin();
-  if (sensor.getAddress(deviceAddress, 0)) {
-    sensor.setResolution(deviceAddress, TEMPERATURE_RESOLUTION);
-    sensor.setWaitForConversion(false);
-    sensor.requestTemperatures(); // start conversion
-  }
-}
-
-/*
- * ReadOWbus()
- */
-TEMP ReadOWbus( DallasTemperature sensor, byte masksensor ) {
-  // Get OW temperature data -- single device connected
-  TEMP temp;
-  //
-  temp = int(100 * sensor.getTempCByIndex(0));  // the unique sensor has index 0
-  sensor.requestTemperatures();                 // restart a conversion
-  // test for bad reading
-  if ( (temp > -5000) && (temp < 12500) ) {
-    ErrSensorClear( masksensor );
-  }
-  else {
-    ErrSensorRaise( masksensor );
-  }
-  return( temp );
-}
-


tap dance fires immediately on last key of dance

--- mods/tapDance/mod_tapDance.ino
+++ mods/tapDance/mod_tapDance.ino
@@ -64,8 +64,7 @@
           }
           else if (modMethod(PressTimer)[i] == 0)
           {
-            modMethod(PressTimer)[i] = -1; // signal that the key has fired
-            PressKey(modMethod(GetKeyVal)(i, modMethod(PressCount)[i] - 1), modMethod(GetKeyType)(i, modMethod(PressCount)[i] - 1));
+            modMethod(FireTapDance)(i);
             if (!modMethod(KeyIsDown)[i])
             {
               modMethod(KeyUp)(i, 19);
@@ -87,19 +86,30 @@
   {
     if (type == 19)
     {
-      // sends the macro sequence with id = val
-      if (modMethod(PressCount)[val] > modMethod(MAX_KEYS))
+      if (modMethod(PressCount)[val] < modMethod(MAX_KEYS))
       {
-        // do nothing
-      }
-      else
-      {
-
-        modMethod(PressCount)[val]++;
+        byte count = ++modMethod(PressCount)[val]; // count gets the *new* count value
         modMethod(PressTimer)[val] = modMethod(GetTimeout)(val) * modMethod(TIMER_MULTIPLIER);
         modMethod(KeyIsDown)[val] = true;
+
+        if (count == modMethod(MAX_KEYS))
+        {
+          // no more tap dance keys; fire immediately
+          modMethod(FireTapDance)(val);
+        }
+        else
+        {
+          // check the key that would come after this one
+          char tdVal = modMethod(GetKeyVal)(val, count);
+          byte tdType = modMethod(GetKeyType)(val, count);
+
+          if (tdVal == 0 && tdType == 0)
+          {
+            // no more tap dance keys; fire immediately
+            modMethod(FireTapDance)(val);
+          }
+        }
       }
-
     }
   }
 }
@@ -110,10 +120,9 @@
   {
     for (byte i = 0; i < modMethod(MAX_INDEX); i++)
     {
-      if ((type != 19 || val != i) && modMethod(PressCount)[i] > 0)
+      if ((type != 19 || val != i) && modMethod(PressTimer)[i] > 0)
       {
-        modMethod(PressTimer)[i] = -1; // signal that the key has fired
-        PressKey(modMethod(GetKeyVal)(i, modMethod(PressCount)[i] - 1), modMethod(GetKeyType)(i, modMethod(PressCount)[i] - 1));
+        modMethod(FireTapDance)(i);
         if (!modMethod(KeyIsDown)[i])
         {
           modMethod(KeyUp)(i, 19);
@@ -133,17 +142,18 @@
   {
     if (type == 19)
     {
-      // sends the macro sequence with id = val
-      ReleaseKey(modMethod(GetKeyVal)(val, modMethod(PressCount)[val] - 1), modMethod(GetKeyType)(val, modMethod(PressCount)[val] - 1));
-
-      // if key has already fired
+      // if key has been fired
       if (modMethod(PressTimer)[val] < 0)
       {
+        byte count = modMethod(PressCount)[val] - 1;
+        char tdVal = modMethod(GetKeyVal)(val, count);
+        byte tdType = modMethod(GetKeyType)(val, count);
+
+        ReleaseKey(tdVal, tdType);
         modMethod(PressCount)[val] = 0;
         modMethod(PressTimer)[val] = 0;
       }
       modMethod(KeyIsDown)[val] = false;
-
     }
   }
 }
@@ -211,6 +221,15 @@

 }

+void modMethod(FireTapDance)(byte id)
+{
+  byte count = modMethod(PressCount)[id] - 1;
+  char val = modMethod(GetKeyVal)(id, count);
+  byte type = modMethod(GetKeyType)(id, count);
+  modMethod(PressTimer)[id] = -1; // signal that the key has fired
+  PressKey(val, type);
+}
+
 byte modMethod(GetLength)(byte id)
 {


Added check in case of shield not present

--- WiFi/examples/ScanNetworks/ScanNetworks.ino
+++ WiFi/examples/ScanNetworks/ScanNetworks.ino
@@ -61,7 +61,12 @@
 void listNetworks() {
   // scan for nearby networks:
   Serial.println("** Scan Networks **");
-  byte numSsid = WiFi.scanNetworks();
+  int numSsid = WiFi.scanNetworks();
+  if (numSsid == -1)
+  {
+    Serial.println("Couldn't get a wifi connection");
+    while(true);
+  }

   // print the list of networks seen:
   Serial.print("number of available networks:");


2.0 = joint control movement + commented preset function


fewer magic numbers, more #define

fewer magic numbers, more #define

--- arduino/personal_space/personal_space.ino
+++ arduino/personal_space/personal_space.ino
@@ -79,8 +79,11 @@
 However this removes the need to do the setup of the nRF8001 on every reset.
 */

+#define SEND_INTERVAL 1000 // milliseconds
+
 // Set up sonar sensors
 #define SONAR_NUM     4    // Number or sensors.
+#define POLL_INTERVAL SEND_INTERVAL % (SONAR_NUM+1)
 #define MAX_DISTANCE  500  // Max distance in cm.
 #define TRIGGER_PIN   7
 #define ECHO_PIN_0    2
@@ -230,9 +233,9 @@
   lib_aci_init(&aci_state, false);

   // Send data every second
-  t.every(1000, send_data);
+  t.every(SEND_INTERVAL, send_data);
   // Collect one sonar sensor every 1/4 sec
-  t.every(200, get_data);
+  t.every(POLL_INTERVAL, get_data);

   Serial.println(F("Set up done"));
 }


Update Syma_servo_trinket.ino

Update Syma_servo_trinket.ino

Adjusted the servo positions after switching to the Trinket & soft_servo library.

--- Syma_servo_trinket.ino
+++ Syma_servo_trinket.ino
@@ -1,3 +1,5 @@
+#include <Adafruit_SoftServo.h>
+
 /*******************************************************************
   SYMA X5C servo control
 *******************************************************************
@@ -37,12 +39,10 @@
   Other bits and pieces were found in the Arduino forums

  *******************************************************************/
-
-#include <Adafruit_SoftServo.h>

-#define SERVO1PIN 0   // Servo control line on Trinket Pin #7
+#define servoPin 0
 int signal = 2; // The signal from the SYMA camera port
-int LEDsignal = 1; // Blink
+int LEDsignal = 1; // LED

 int current;         // Current state of the button
                      // (LOW is pressed b/c i'm using the pullup resistors)
@@ -63,11 +63,11 @@
   digitalWrite(LEDsignal, LOW);

- OCR0A = 0xAF;            // any number is OK
- TIMSK |= _BV(OCIE0A);    // Turn on the compare interrupt (below!)
+OCR0A = 0xAF;            // any number is OK
+TIMSK |= _BV(OCIE0A);    // Turn on the compare interrupt (below!)

-  myServo1.attach(SERVO1PIN);   // Attach the servo to pin 0 on Trinket
-  myServo1.write(165);          // Initial position (horizontal)
+  myServo1.attach(servoPin);   // Attach the servo to pin 0 on Trinket
+  myServo1.write(180);          // Initial position (horizontal)
   delay(15);                   // Wait 15ms for the servo to reach the position
 }

@@ -94,31 +94,34 @@
       //----------------- Controlling the Servo ------------------
       //----------------------------------------------------------

-      // Low signal from Camera, 0.15 - 0.35s (picture button)
+      // PICTURE BUTTON (Low signal from Camera, 0.15 - 0.35s)
       if (millis_held > 150  && millis_held < 350) {

         //Move from 0 to -90 degrees (horizontal to straight down)
         if(toggle){
-          myServo1.write(70);
+          myServo1.write(0);
           toggle = !toggle;
-          digitalWrite(LEDsignal, HIGH);   // turn the LED on
-          delay(500);                       // wait for half a second
-          digitalWrite(LEDsignal, LOW);    // turn the LED off
+          digitalWrite(LEDsignal, HIGH);   // turn the LED on (HIGH is the voltage level)
+          delay(500);                       // wait
+          digitalWrite(LEDsignal, LOW);    // turn the LED off by making the voltage LOW
         }
         //Move from 90 to 0 degrees (straight down to horizontal)
         else{
-          myServo1.write(165);
+          myServo1.write(180);
           toggle = !toggle;
-          digitalWrite(LEDsignal, HIGH);   // turn the LED on
-          delay(500);                       // wait for half a second
-          digitalWrite(LEDsignal, LOW);    // turn the LED off
+          digitalWrite(LEDsignal, HIGH);   // turn the LED on (HIGH is the voltage level)
+          delay(500);                       // wait
+          digitalWrite(LEDsignal, LOW);    // turn the LED off by making the voltage LOW
         }
-      } //0.25s end_if
+      } //end_if picture

-     // Low signal from Camera is >0.5s (video button)
+     // VIDEO BUTTON (low signal from Camera, ~0.75s)
       if (millis_held > 500) {
-        //whatever we want... haven't decided yet
-      }//0.75s end_if
+        //whatever we want... for now let's just show it works
+         digitalWrite(LEDsignal, HIGH);   // turn the LED on (HIGH is the voltage level)
+         delay(1500);                       // wait
+         digitalWrite(LEDsignal, LOW);    // turn the LED off by making the voltage LOW
+      }//end_if video

       //----------------------------------------------------------
       //----------------------------------------------------------
@@ -139,12 +142,12 @@
       // Called by the microcontroller every 2 milliseconds

     volatile uint8_t counter = 0;
-    SIGNAL(TIMER0_COMPA_vect) {
-      // this gets called every 2 milliseconds
-        counter += 2;
-        // every 20 milliseconds, refresh the servos!
-        if (counter >= 20) {
-        counter = 0;
-        myServo1.refresh();
-            }
-        }
+SIGNAL(TIMER0_COMPA_vect) {
+  // this gets called every 2 milliseconds
+  counter += 2;
+  // every 20 milliseconds, refresh the servos!
+  if (counter >= 20) {
+    counter = 0;
+    myServo1.refresh();
+  }
+}


Update GPRS_ConnectTCP.ino

Update GPRS_ConnectTCP.ino

--- example/GPRS_ConnectTCP/GPRS_ConnectTCP.ino
+++ example/GPRS_ConnectTCP/GPRS_ConnectTCP.ino
@@ -52,6 +52,7 @@

 void setup() {
   Serial.begin(9600);
+  while(!Serial);;
   Serial.println("GPRS - TCP Connection Test...");
   pinMode(soundSensor,INPUT);
   gprs.preInit();


Switched over to time.h library, still some bugs

--- Arduino_HVAC_Controller/Networked_HVAC_Controller/Networked_HVAC_Controller.ino
+++ Arduino_HVAC_Controller/Networked_HVAC_Controller/Networked_HVAC_Controller.ino
@@ -20,7 +20,7 @@
 #include <SPI.h>                  // Needed for the Ethernet shield
 #include <Ethernet.h>             // Enables internet connectivity, needs pins 10 11 12 13
 #include <EthernetUdp.h>          // Needed for the Ethernet shield
-
+#include <Time.h>                 // Needed for easier time functionality

@@ -54,11 +54,18 @@
 IPAddress timeServer(132, 163, 4, 101);     // time-a.timefreq.bldrdoc.gov NTP server
 // IPAddress timeServer(132, 163, 4, 102);  // time-b.timefreq.bldrdoc.gov NTP server
 // IPAddress timeServer(132, 163, 4, 103);  // time-c.timefreq.bldrdoc.gov NTP server
-const int NTP_PACKET_SIZE= 48;              // NTP time stamp is in the first 48 bytes of the message
-byte packetBuffer[ NTP_PACKET_SIZE];        //buffer to hold incoming and outgoing packets
 EthernetUDP Udp;                            // A UDP instance to let us send and receive packets over UDP
 unsigned long hoursSeconds = 0;             // Return variable for the get_Time function
 unsigned long timeDelay = 0;                // Counter to prevent time from getting retrieved too often
+const int timeZone = -5;                    // Eastern Standard Time (USA)
+time_t prevDisplay = 0;                     // TODO REMOVE _________________________________
+
+
+// --------------- NTP Variables ---------------
+const int NTP_PACKET_SIZE = 48;             // NTP time is in the first 48 bytes of message
+byte packetBuffer[NTP_PACKET_SIZE];         //buffer to hold incoming & outgoing packets
+
+

 // --------------- LCD Shield Variables ---------------
@@ -180,22 +187,12 @@

   // --------------Handle updating Temp and Time change display---------------------
   if ((millis() - timeDelay) > 10000) {    // Update every 10 seconds
-    lcd.setCursor(0, 0);                  // Starting postion of character printing
+    lcd.setCursor(5, 0);                  // Starting postion of character printing
     lcd.clear();
-    lcd.print("Current Temp: ");
-    get_temp();
-    lcd.print(currentTemp);
-
-    hoursSeconds = get_time();
-    lcd.setCursor(5, 1);
-    // print the hour and minute:
-    lcd.print((hoursSeconds  % 86400L) / 3600); // print the hour (86400 equals secs per day)
-    lcd.print(':');
-    if ( ((hoursSeconds % 3600) / 60) < 10 ) {
-        // In the first 10 minutes of each hour, we'll want a leading '0'
-        lcd.print('0');
-    }
-    lcd.print((hoursSeconds  % 3600) / 60); // print the minute (3600 equals secs per minute)
+    lcd.print("Temp: ");
+    lcd.print(get_temp());
+    lcd.setCursor(0, 1);
+    lcd.print(get_time());

     timeDelay = millis();
   }
@@ -345,7 +342,7 @@
 // Returns:       Boolean, True if the temperature has changed, else False
 // Description:   This function polls the temp sensors, retrieves the values, and then returns
 // ----------------------------------------------------------------------------------------
-boolean get_temp(){
+int get_temp(){
   currentTemp = 0;
   long temp = 0;
   sensors.requestTemperatures(); // Send the command to the device to get temperatures
@@ -358,14 +355,7 @@
     }
   }

-  // Check to see if temperature has changed
-  if (currentTemp != oldTemp) {
-    oldTemp = currentTemp;
-    return true;
-  }
-  else {
-    return false;
-  }
+  return currentTemp;
 }

@@ -377,18 +367,20 @@
 // ----------------------------------------------------------------------------------------
 void setup_network() {

-  // TODO: _________________________REMOVE ALL Serial stuff when it works________________________
-
-  // Open serial communications and wait for port to open:
   Serial.begin(serialSpeed);
-
-  // start Ethernet and UDP
+  delay(250);
   if (Ethernet.begin(mac) == 0) {
-    Serial.println("Failed to configure Ethernet using DHCP");
     // no point in carrying on, so do nothing forevermore:
-    while(1){}
-  }
+    while (1) {
+      //Serial.println("Failed to configure Ethernet using DHCP");
+      delay(10000);
+    }
+  }
+  //Serial.print("IP number assigned by DHCP is ");
+  Serial.println(Ethernet.localIP());
   Udp.begin(localPort);
+  //Serial.println("waiting for sync");
+  setSyncProvider(getNtpTime);
 }

 // ----------------------------------------------------------------------------------------
@@ -397,44 +389,27 @@
 // Returns:       An unsigned long representing time in hours and minutes
 // Description:   TODO ____________________________________________________
 // ----------------------------------------------------------------------------------------
-char* get_time()
-{
-  sendNTPpacket(timeServer); // send an NTP packet to a time server
-  unsigned long epoch;
-
-    // wait to see if a reply is available
-  delay(1000);
-  if ( Udp.parsePacket() ) {
-    // We've received a packet, read the data from it
-    Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer
-
-    //the timestamp starts at byte 40 of the received packet and is four bytes,
-    // or two words, long. First, esxtract the two words:
-
-    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
-    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
-    // combine the four bytes (two words) into a long integer
-    // this is NTP time (seconds since Jan 1 1900):
-    unsigned long secsSince1900 = highWord << 16 | lowWord;
-
-    // now convert NTP time into everyday time:
-    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
-    const unsigned long seventyYears = 2208988800UL;
-    // subtract seventy years:
-    epoch = secsSince1900 - seventyYears - (5 * 3600);
-
+String get_time() {
+  String timeString = "";
+    if (timeStatus() != timeNotSet) {
+      timeString = hour() + ":";
+      if(minute() < 10) {
+        timeString = timeString + "0";
+      }
+      timeString = timeString + minute() + "  " + month() + " " + day() + " " + year();
   }
   return timeString;
 }
+
+

 // ----------------------------------------------------------------------------------------
 // Function Name: sendNTPpacket(IPAddress& address)
 // Parameters:    IP Address
-// Returns:       Unsigned Long
+// Returns:       None
 // Description:   send an NTP request to the time server at the given address
 // ----------------------------------------------------------------------------------------
-unsigned long sendNTPpacket(IPAddress& address)
-{
+void sendNTPpacket(IPAddress &address) {
   // set all bytes in the buffer to 0
   memset(packetBuffer, 0, NTP_PACKET_SIZE);
   // Initialize values needed to form NTP request
@@ -448,10 +423,39 @@
   packetBuffer[13]  = 0x4E;
   packetBuffer[14]  = 49;
   packetBuffer[15]  = 52;
-
   // all NTP fields have been given values, now
-  // you can send a packet requesting a timestamp:
+  // you can send a packet requesting a timestamp:
   Udp.beginPacket(address, 123); //NTP requests are to port 123
-  Udp.write(packetBuffer,NTP_PACKET_SIZE);
+  Udp.write(packetBuffer, NTP_PACKET_SIZE);
   Udp.endPacket();
 }
+
+// ----------------------------------------------------------------------------------------
+// Function Name: getNtpTime()
+// Parameters:    None
+// Returns:       Variable time_t, a time object
+// Description:   TODO
+// ----------------------------------------------------------------------------------------
+time_t getNtpTime()
+{
+  while (Udp.parsePacket() > 0) ; // discard any previously received packets
+  //Serial.println("Transmit NTP Request");
+  sendNTPpacket(timeServer);
+  uint32_t beginWait = millis();
+  while (millis() - beginWait < 1500) {
+    int size = Udp.parsePacket();
+    if (size >= NTP_PACKET_SIZE) {
+      //Serial.println("Receive NTP Response");
+      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
+      unsigned long secsSince1900;
+      // convert four bytes starting at location 40 to a long integer
+      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
+      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
+      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
+      secsSince1900 |= (unsigned long)packetBuffer[43];
+      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
+    }
+  }
+  //Serial.println("No NTP Response :-(");
+  return 0; // return 0 if unable to get the time
+}


Converted for HD

Converted for HD

--- examples/chess.ino
+++ examples/chess.ino
@@ -4,6 +4,8 @@

 #include "chess_assets.h"

+byte sf = 1;
+
 static byte sinus(byte x)
 {
   return 128 + GD.rsin(128, -16384 + (x << 7));
@@ -14,9 +16,14 @@
   Serial.begin(1000000);    // JCB
   GD.begin(~GD_STORAGE);
   LOAD_ASSETS();
+
+  sf = GD.h / 272;
+
+  GD.BitmapHandle(0);
+  GD.BitmapSize(BILINEAR, BORDER, BORDER, sf * 32, sf * 32);
   GD.BitmapHandle(1);
   GD.BitmapSource(CHECKER);
-  GD.BitmapSize(NEAREST, REPEAT, REPEAT, 256, 256);
+  GD.BitmapSize(NEAREST, REPEAT, REPEAT, sf * 256, sf * 256);
   GD.BitmapLayout(L8, CHECKER_WIDTH, CHECKER_HEIGHT);
 }

@@ -221,11 +228,11 @@

 static void p2(int &x, int &y, const char *c)
 {
-  x = 16 * (32 + 32 * (c[0] - 'a'));
-  y = 16 * ( 8 + 32 * ('8' - c[1]));
-}
-
-#define MOVETIME    10
+  x = 16 * (32 + sf * 32 * (c[0] - 'a'));
+  y = 16 * ( 8 + sf * 32 * ('8' - c[1]));
+}
+
+#define MOVETIME    30
 #define OVERSAMPLE  8

 int clocks[2];
@@ -236,14 +243,14 @@
   byte xlat[] = { 0, 0, 0, 1, 5, 2, 3, 4, 0, 6, 0, 7, 11,8, 9,10 };

   for (int i = 0; i < MOVETIME; i++) {
-    GD.cmd_gradient(0, 0, 0x101010, 480, 272, 0x202060);
+    GD.cmd_gradient(0, 0, 0x101010, GD.w, GD.h, 0x202060);
     GD.cmd_bgcolor(0x101020);

     GD.Begin(BITMAPS);
     GD.SaveContext();
     GD.ColorRGB(0xfff0c0);
     GD.ColorA(0xc0);
-    GD.cmd_scale(F16(32), F16(32));
+    GD.cmd_scale(F16(sf * 32), F16(sf * 32));
     GD.cmd_setmatrix();
     GD.Vertex2ii(32, 8, 1, 0);
     GD.RestoreContext();
@@ -252,14 +259,17 @@
     if (c[0])
       moving = 16 * ('8' - c[3]) + (c[2] - 'a');

-    GD.Begin(BITMAPS);
+    GD.SaveContext();
     GD.ColorRGB(0xe0e0e0);
+    GD.cmd_loadidentity();
+    GD.cmd_scale(F16(sf), F16(sf));
+    GD.cmd_setmatrix();
     for (int y = 0; y < 8; y++)
       for (int x = 0; x < 8; x++) {
         int pos = 16 * y + x;
         byte piece = b[pos] & 15;
         if (pos != moving && piece != 0)
-          GD.Vertex2ii(32 + 32 * x, 8 + 32 * y, 0, xlat[piece]);
+          GD.Vertex2ii(32 + sf * 32 * x, 8 + sf * 32 * y, 0, xlat[piece]);
       }

     if (c[0]) {
@@ -282,14 +292,15 @@
       } //' }a
       GD.ColorA(255);
     }
+    GD.RestoreContext();

     GD.ColorRGB((k == 16) ? 0xffffffUL : 0x606060);
-    GD.cmd_clock(384,       60, 50, OPT_FLAT | OPT_NOSECS, 0, 0, clocks[0], 0);
+    GD.cmd_clock(sf * 384, sf *  60, sf * 50, OPT_FLAT | OPT_NOSECS, 0, 0, clocks[0], 0);
     GD.ColorRGB((k != 16) ? 0xffffffUL : 0x606060);
-    GD.cmd_clock(384, 272 - 60, 50, OPT_FLAT | OPT_NOSECS, 0, 0, clocks[1], 0);
+    GD.cmd_clock(sf * 384, sf * (272 - 60), sf * 50, OPT_FLAT | OPT_NOSECS, 0, 0, clocks[1], 0);

     GD.ColorRGB(0xffffff);
-    GD.cmd_text(384, 136, 30, OPT_CENTER, c);
+    GD.cmd_text(sf * 384, sf * 136, 30, OPT_CENTER, c);

     GD.swap();
   }


Ripple effect of LCDKeymap refactoring.

Ripple effect of LCDKeymap refactoring.

--- libraries/LCD/CosaLCDkeypad/CosaLCDkeypad.ino
+++ libraries/LCD/CosaLCDkeypad/CosaLCDkeypad.ino
@@ -29,17 +29,18 @@
 #include "Cosa/Types.h"
 #include "Cosa/Event.hh"
 #include "Cosa/Keypad.hh"
+#include "Cosa/LCD.hh"
 #include "Cosa/IOStream.hh"
 #include "Cosa/Watchdog.hh"

 #include <HD44780.h>

-class KeypadTrace : public LCDKeypad {
+class KeypadTrace : public LCD::Keypad {
 private:
   IOStream m_out;
 public:
   KeypadTrace(Job::Scheduler* scheduler, IOStream::Device* dev) :
-    LCDKeypad(scheduler),
+    LCD::Keypad(scheduler),
     m_out(dev)
   {}


Reading Vcc and internal Arduino temperature sensor

--- TemperatureMonitor/TemperatureMonitor.ino
+++ TemperatureMonitor/TemperatureMonitor.ino
@@ -1,6 +1,7 @@
 #include "DHT.h"

 int sampleRate = 2500; //Milliseconds between sampling
+float tCorrection = -9.4;

 #define DHTPWR 7
 #define DHTGND 4
@@ -41,6 +42,9 @@
   float tC = dht.readTemperature();
   float tF = tC * 9 / 5 + 32;
   float l = 0;
+
+  float tArduino = readTemp()/1000.0 + tCorrection;
+  float vArduino = readVcc()/1000.0;

   // check if returns are valid, if they are NaN (not a number) then something went wrong!
   if (isnan(tC) || isnan(h)) {
@@ -67,9 +71,39 @@
     Serial.print("|");
     Serial.print(l);
     Serial.print("|");
-    Serial.print("(Celsius,Fahrenheit,% Humidity,Light)");
+    Serial.print(vArduino);
+    Serial.print("|");
+    Serial.print(tArduino);
+    Serial.print("|");
+    Serial.print("(C,F,%,lm,Vcc,Ci)");
     Serial.println();
   }

   delay(sampleRate);
 }
+
+long readTemp() {
+  long result;
+  // Read temperature sensor against 1.1V reference
+  ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3);
+  delay(2); // Wait for Vref to settle
+  ADCSRA |= _BV(ADSC); // Convert
+  while (bit_is_set(ADCSRA,ADSC));
+  result = ADCL;
+  result |= ADCH<<8;
+  result = (result - 125) * 1075;
+  return result;
+}
+
+long readVcc() {
+  long result;
+  // Read 1.1V reference against AVcc
+  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
+  delay(2); // Wait for Vref to settle
+  ADCSRA |= _BV(ADSC); // Convert
+  while (bit_is_set(ADCSRA,ADSC));
+  result = ADCL;
+  result |= ADCH<<8;
+  result = 1126400L / result; // Back-calculate AVcc in mV
+  return result;
+}


Dependancy spelling mistake

Dependancy spelling mistake

Just in case someone is having a hard time finding the packages

--- bruh_mqtt_multisensor_github/bruh_mqtt_multisensor_github.ino
+++ bruh_mqtt_multisensor_github/bruh_mqtt_multisensor_github.ino
@@ -16,7 +16,7 @@

   - You will also need to download the follow libraries by going to Sketch -> Include Libraries -> Manage Libraries
       - DHT sensor library
-      - Adfruit unified sensor
+      - Adafruit unified sensor
       - PubSubClient
       - ArduinoJSON


Fix comments.

Fix comments.

--- examples/ContinuousMode/ContinuousMode.ino
+++ examples/ContinuousMode/ContinuousMode.ino
@@ -27,14 +27,14 @@
   // If not one of the above, the closest lower value will be selected.
   adc.setSampleRate(860);

-  // Enter continuous mode reading channel 0 (input A0)
+  // Enter continuous mode and start reading channel 0 (input A0)
   adc.setContinuous(true);
   adc.readADC_SingleEnded(channel);

   Serial.print("Configured sample rate: ");
   Serial.print(adc.getSampleRate());
   Serial.println(" SPS");
-  Serial.println("The actual sample rate will be lower because of I2C communication time");
+  Serial.println("The actual sample rate will be lower because of I2C communications");
 }

 void loop()
@@ -43,11 +43,11 @@
   unsigned long sum = 0;       // ADC value sum
   unsigned long t0 = millis(); // Sampling start time

-  // Sample continuously during one second
+  // Sample continuously for one second
   while (millis() - t0 < 1000) {
-    // Wait for conversion. This is optional as adc.readNext() already does
-    //   it, but it can be used in a non-blocking way to check the ADC status
-    //   periodically and do something else the conversion is being done.
+    // Wait for conversion. This is optional, as adc.readNext() already does
+    //   it, but it can be used to check the ADC status periodically in a non-
+    //   blocking way and do something else while the conversion is being done.
     while (!adc.conversionDone());

     // Read ADC value


LoaderTest: integrate PS/2 keyboard in a simply way (good enough for Snake)

TODO:
- Add Ctrl/Alt/Del to reset system (now page up is abused)
- Detecting holding down of keys (so you can play Racer)


change test to public namespace, fix timezone bug

--- garage_status.ino
+++ garage_status.ino
@@ -3,8 +3,6 @@

 // This #include statement was automatically added by the Particle IDE.
 #include "neopixel/neopixel.h"
-
-#include <string.h>

 // ---- CHANGE THIS ----
 #define PIXEL_PIN D1
@@ -15,17 +13,24 @@

 int doorState = -1;

-// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
-// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
-// and minimize distance between Arduino and first pixel.  Avoid connecting
-// on a live circuit...if you must, connect GND first.
+// don't run during "the day", define what that means here
+int blackoutStart = 3;  // 3am
+int blackoutEnd  = 21; // 6pm
+// I'd love to use the positive inflection here but it's too hard to
+// implement without a real time/date library.  You can't easily
+// represent like 3am -- meaning the next day with a number.
+

 void setup() {
-  Serial.begin(9600);
+  // Serial.begin(9600);
+
+  Time.zone(-7);  // PDT
+
   strip.begin();
   strip.show(); // Initialize all pixels to 'off'
   Particle.subscribe("squarism/garage/open", eventHandler);
-  Serial.println("I'm booted!");
+
+  // Serial.println("I'm booted!");
 }

 void loop() {
@@ -33,50 +38,72 @@
     Particle.connect();
   }
   Particle.process();
-
-  if (doorState == 1) {
-    fireRing(200);
-  } else if (doorState == 0) {
-    iceRing(200);
+
+  if (isOperatingTime()) {
+    switch(doorState)
+    {
+      case 1:
+        fireRing(200);
+        break;
+      case 0:
+        iceRing(200);
+        break;
+    }
+  } else {
+    // sort of a sleep mode here
+    blankStrip();
+    delay(60000);
   }
 }

 void eventHandler(const char *event, const char *data) {
-    // Parse the event as json into something structured so we can react to it.
-    // Saved by example!
-    // https://github.com/glowfishAPI/glowfish-particle/blob/master/firmware/examples/anomaly-detect-webhook/anomaly-detect-webhook.ino
-    int len = strlen(data);
-    char dataCopy[len+1];
-    strcpy(dataCopy, data);
+  // Parse the event as json into something structured so we can react to it.
+  // Saved by example!
+  // https://github.com/glowfishAPI/glowfish-particle/blob/master/firmware/examples/anomaly-detect-webhook/anomaly-detect-webhook.ino
+  int len = strlen(data);
+  char dataCopy[len+1];
+  strcpy(dataCopy, data);

-    StaticJsonBuffer<200> jsonBuffer;
-    JsonObject& json = jsonBuffer.parseObject(dataCopy);
-
-    if (json["door_open"] == true) {
-        Serial.println("door is open.");
-        doorState = 1;
-    }
+  StaticJsonBuffer<200> jsonBuffer;
+  JsonObject& json = jsonBuffer.parseObject(dataCopy);

-    if (json["door_open"] == false) {
-        Serial.println("door is closed.");
-        doorState = 0;
-    }
+  if (json["door_open"] == true) {
+    doorState = 1;
+  }

+  if (json["door_open"] == false) {
+    doorState = 0;
+  }
+}
+
+bool isOperatingTime() {
+  if (!((Time.hour() >= blackoutStart) && (Time.hour() <= blackoutEnd))) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+void blankStrip() {
+  uint32_t black = strip.Color(0, 0, 0);
+  for(uint16_t i=0; i<strip.numPixels(); i++) {
+    strip.setPixelColor(i, black);
+  }
 }

 // Fill the dots one after the other with a color
 void colorWipe(uint32_t c, uint8_t wait) {
   for(uint16_t i=0; i<strip.numPixels(); i++) {
-      strip.setPixelColor(i, c);
-      strip.show();
-      delay(wait);
+    strip.setPixelColor(i, c);
+    strip.show();
+    delay(wait);
   }
 }

 void fireRing(uint16_t wait) {
   uint16_t i, j;
   int colorStart = 95;  // a pure red
-  int colorEnd = 125;    // the led starts to blink purple
+  int colorEnd = 125;   // the led starts to blink purple

   for(j=colorStart; j<colorEnd; j++) {
     for(i=0; i<strip.numPixels(); i++) {
@@ -97,8 +124,8 @@

 void iceRing(uint16_t wait) {
   uint16_t i, j;
-  int colorStart = 166;
-  int colorEnd = 199;
+  int colorStart = 166;  // blue
+  int colorEnd = 199;    // blue-green

   for(j=colorStart; j<colorEnd; j++) {
     for(i=0; i<strip.numPixels(); i++) {
@@ -122,13 +149,13 @@
 // The colours are a transition r - g - b - back to r.
 uint32_t Wheel(byte WheelPos) {
   if(WheelPos < 85) {
-   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
+    return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
   } else if(WheelPos < 170) {
-   WheelPos -= 85;
-   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
+    WheelPos -= 85;
+    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
   } else {
-   WheelPos -= 170;
-   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
+    WheelPos -= 170;
+    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
   }
 }


Sending notification from loop

Sending notification from loop

--- ESP32/ble_button/ble_button.ino
+++ ESP32/ble_button/ble_button.ino
@@ -7,13 +7,13 @@
 #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

 #define INPUT_PIN 33
-#define DEBOUNCE_TIME_MICROS 100000
+#define DEBOUNCE_TIME_MILLIS 50

 BLEServer* pServer = NULL;
 BLECharacteristic* pCharacteristic = NULL;

 bool connected = false;
-unsigned long lastValidReadTime = 0;
+bool pendingValue = false;

 class BLECallback: public BLEServerCallbacks {
@@ -60,43 +60,26 @@
 }

-/** Debounce
- *
- *  Returns true if this is the first reading after a switch.
- */
-bool debounce(){
-  unsigned long readTime = micros();
-  if(readTime - lastValidReadTime > DEBOUNCE_TIME_MICROS){
-    lastValidReadTime = readTime;
-    return true;
+void notify(){
+  if(connected == true){
+    // Read input and store
+    uint8_t value =  0;
+    if(digitalRead(INPUT_PIN) == HIGH){
+      value = 1;
+    }
+
+    // Send 1 byte of data with value
+    pCharacteristic->setValue((uint8_t*)&value, 1);
+    pCharacteristic->notify();
+    Serial.println("Notification sent");
   }
   else{
-    return false;
+    Serial.println("Value not sent. Not connected.");
   }
 }

-
-void onPinChanged(){
-  Serial.println("On pin changed");
-  if(debounce() == true){
-    // Wait until value has estabilized
-    delay((unsigned long)DEBOUNCE_TIME_MICROS/1000);
-    if(connected == true){
-      // Read input and store
-      uint8_t value =  0;
-      if(digitalRead(INPUT_PIN) == HIGH){
-        value = 1;
-      }
-
-      // Send 1 byte of data with value
-      pCharacteristic->setValue((uint8_t*)&value, 1);
-      pCharacteristic->notify();
-      Serial.println("Notification sent");
-    }
-    else{
-      Serial.println("Value not sent. Not connected.");
-    }
-  }
+void IRAM_ATTR onPinChanged(){
+  pendingValue = true;
 }

@@ -115,6 +98,11 @@

 void loop() {
-  delay(10000);
-  Serial.println("loop");
+  if(pendingValue == true){
+    // Wait for pin to stabilize
+    delay(DEBOUNCE_TIME_MILLIS);
+    notify();
+    pendingValue = false;
+  }
+  delay(10);
 }


Fix clutch parsing

Fix clutch parsing

--- Event_Printer/Event_Printer.ino
+++ Event_Printer/Event_Printer.ino
@@ -148,7 +148,7 @@
     Serial.println();

     // CLUTCH LEVER
-    unsigned char hnibble5 = HI_NIBBLE(data[4]);
+    unsigned char hnibble5 = LO_NIBBLE(data[4]);
     Serial.print("Clutch: ");
     if( hnibble5 == 0x0A ) Serial.print("IN");
     else if( hnibble5 == 0x06 ) Serial.print("OUT");


Bugfixes: - increment pointer in print function (otherwise gets stuck in a loop) - allow ping to any address (including broadcast address)

--- arduino/rflink/rflink.ino
+++ arduino/rflink/rflink.ino
@@ -62,7 +62,7 @@
     // send it to serial
     char *p = buf;
     while (*p != 0) {
-        serial_putc(*p);
+        serial_putc(*p++);
     }
 }

@@ -120,9 +120,6 @@
     uint8_t node = ADDR_BROADCAST;
     if (argc > 1) {
         node = atoi(argv[1]);
-    }
-    if (!node_valid(node)) {
-        return ERR_PARAM;
     }

     // prepare ping message


Refactored example

Refactored example

Signed-off-by: Roman3349 <6b7cbc66756660b4790b3af1ec1507653acaa035@centrum.cz>

--- examples/Init/Init.ino
+++ examples/Init/Init.ino
@@ -22,25 +22,26 @@
 #define USER_TIMER_PERIOD 5000

 // LOCAL PROTOTYPES
-void MyIqrfRxHandler(void);
-void MyIqrfTxHandler(uint8_t txPktId, uint8_t txPktResult);
-void AppTimerHandler(void);
-void cb_timer1ms(void);
+void rxHandler();
+void txHandler(uint8_t packetId, uint8_t packetResult);
+void msTimerCallback();

 // GLOBAL VARIABLES
-// App data
+
+/**
+ * App data structure
+ */
 typedef struct {
-  uint8_t myIqrfRxBuf[IQ_PKT_SIZE];
-  uint8_t *myIqrfTxBuf;
-  uint8_t testPktId;
-  volatile uint16_t appTimer;
-  volatile uint8_t appTimerAck;
-} app_vars_t;
-app_vars_t app_vars;
+	uint8_t rxBuffer[IQ_PKT_SIZE];
+	uint8_t *txBuffer;
+	uint8_t packetId;
+	volatile uint16_t timer;
+	volatile bool timerAck;
+} appVarsStruct;
+appVarsStruct appVars;

 // Const data
 const uint8_t testBuffer[64] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64};
-const uint8_t ledPin = 13;

 // INSTANCES
 // IQRFTR instance
@@ -50,89 +51,82 @@
  * Init peripherals
  */
 void setup() {
-  // User LED
-  pinMode(ledPin, OUTPUT);
-  // Up - PC
-  Serial.begin(9600);
-  // Down - IQRF
-  IQRF_Init(MyIqrfRxHandler, MyIqrfTxHandler);
-  // Info - TR
-  switch (IQRF_GetModuleType()) {
-    case iqrfTr->types::TR_52D:
-      Serial.println("Module type: TR-52D");
-      break;
-    case iqrfTr->types::TR_72D:
-      Serial.println("Module type: TR-72D");
-      break;
-    default:
-      Serial.println("Module type: UNKNOWN");
-      break;
-  }
-  MsTimer2::set(1, cb_timer1ms);
-  MsTimer2::start();
-  // Clear variables
-  memset(&app_vars, 0, sizeof(app_vars_t));
-  app_vars.appTimer = USER_TIMER_PERIOD;
-  // Done here
-  Serial.println("Peripherals and IQRF init done");
+	// User LED
+	pinMode(13, OUTPUT);
+	// Up - PC
+	Serial.begin(9600);
+	// Down - IQRF
+	IQRF_Init(rxHandler, txHandler);
+	// Info - TR
+	switch (IQRF_GetModuleType()) {
+		case iqrfTr->types::TR_52D:
+			Serial.println("Module type: TR-52D");
+			break;
+		case iqrfTr->types::TR_72D:
+			Serial.println("Module type: TR-72D");
+			break;
+		default:
+			Serial.println("Module type: UNKNOWN");
+			break;
+	}
+	MsTimer2::set(1, msTimerCallback);
+	MsTimer2::start();
+	// Clear variables
+	memset(&appVars, 0, sizeof (appVarsStruct));
+	appVars.timer = USER_TIMER_PERIOD;
+	// Done here
+	Serial.println("Peripherals and IQRF init done");
 }

 /**
  * Main loop
  */
 void loop() {
-  // TR module SPI comunication driver
-  IQRF_Driver();
-  // Test send data every 5s
-  if (app_vars.appTimerAck) {
-    // Allocate memory for Tx packet
-    app_vars.myIqrfTxBuf = (uint8_t *) malloc(sizeof(testBuffer));
-    if (app_vars.myIqrfTxBuf != NULL) {
-      // Copy data from test to IQRF TX packet
-      memcpy(app_vars.myIqrfTxBuf, (uint8_t *) &testBuffer, sizeof(testBuffer));
-      // Send data and unallocate data buffer
-      app_vars.testPktId = IQRF_SendData(app_vars.myIqrfTxBuf, sizeof(testBuffer), 1);
-    }
-    app_vars.appTimerAck = false;
-  }
+	// TR module SPI comunication driver
+	IQRF_Driver();
+	// Test send data every 5s
+	if (appVars.timerAck) {
+		// Allocate memory for Tx packet
+		appVars.txBuffer = (uint8_t *) malloc(sizeof (testBuffer));
+		if (appVars.txBuffer != NULL) {
+			// Copy data from test to IQRF TX packet
+			memcpy(appVars.txBuffer, (uint8_t *) & testBuffer, sizeof (testBuffer));
+			// Send data and unallocate data buffer
+			appVars.packetId = IQRF_SendData(appVars.txBuffer, sizeof (testBuffer), 1);
+		}
+		appVars.timerAck = false;
+	}
 }

 /**
  * 1ms timer callback
  */
-void cb_timer1ms() {
-  // app timer, call handler
-  if (app_vars.appTimer) {
-    if ((--app_vars.appTimer) == 0) {
-      AppTimerHandler();
-      app_vars.appTimer = USER_TIMER_PERIOD;
-    }
-  }
-}
-
-/**
- * User timer handler
- */
-void AppTimerHandler() {
-  app_vars.appTimerAck = true;
+void msTimerCallback() {
+	// app timer, call handler
+	if (appVars.timer) {
+		if ((--appVars.timer) == 0) {
+			appVars.timerAck = true;
+			appVars.timer = USER_TIMER_PERIOD;
+		}
+	}
 }

 /**
  * IQRF RX callback
  */
-void MyIqrfRxHandler() {
-  // Read and print received data
-  IQRF_GetRxData(app_vars.myIqrfRxBuf, IQRF_GetRxDataSize());
-  Serial.print("IQRF receive done: ");
-  Serial.write(app_vars.myIqrfRxBuf, IQRF_GetRxDataSize());
-  Serial.println();
+void rxHandler() {
+	// Read and print received data
+	IQRF_GetRxData(appVars.rxBuffer, IQRF_GetRxDataSize());
+	Serial.print("IQRF receive done: ");
+	Serial.write(appVars.rxBuffer, IQRF_GetRxDataSize());
+	Serial.println();
 }

 /**
  * IQRF TX callback
- * @param txPktId Paket ID
- * @param txPktResult Paket writing result
+ * @param packetId Packet ID
+ * @param packetResult Packet writing result
  */
-void MyIqrfTxHandler(uint8_t txPktId, uint8_t txPktResult) {
-  Serial.println("IQRF send done");
+void txHandler(uint8_t packetId, uint8_t packetResult) {
+	Serial.println("IQRF send done");
 }