Some cosmetic cleanups to header.

Some cosmetic cleanups to header.

--- keg_scribe.ino
+++ keg_scribe.ino
@@ -27,8 +27,6 @@
 BSD license, check license.txt for more information
 All text above must be included in any redistribution
 **********************************************************/
-//#include "LiquidCrystal.h"
-//LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

 const char OK_MSG[]   = " ok.\r\n";
 const char FAIL_MSG[] = " fail.\r\n";
@@ -36,21 +34,19 @@
 // which pin to use for reading the sensor? can use any pin!
 #define TEMPERATURE1_ANALOG_PIN 0
 #define TEMPERATURE2_ANALOG_PIN 1
-#define FLOWSENSOR_DIGITAL_PIN 2
+#define FLOWSENSOR_DIGITAL_PIN  2

 const char TEMPERATURE1_IMPORT_CODE[] = "KegScribeAmbientTemperature";
 const char TEMPERATURE2_IMPORT_CODE[] = "KegScribeFridgeTemperature";
-const char TAP1_IMPORT_CODE[] = "KegScribeTap1";
-const char TAP2_IMPORT_CODE[] = "KegScribeTap2";
-
-// number of milliseconds between recording values
-#define LOOP_INTERVAL (1000)
+const char TAP1_IMPORT_CODE[]         = "KegScribeTap1";
+const char TAP2_IMPORT_CODE[]         = "KegScribeTap2";
+
 // number of milliseconds between recording to SD card
-#define RECORD_INTERVAL (1000UL*10UL*1UL)
+#define RECORD_INTERVAL (1000UL*60UL*1UL)
 // number of milliseconds between reports
 #define REPORT_INTERVAL (1000UL*60UL*5UL)
 // number of seconds between calls to the NTP server
-#define NTP_INTERVAL (60*60*24)
+#define NTP_INTERVAL    (60*60*24)

 // count how many pulses!
 volatile uint16_t pulses = 0;


スリープ間隔と前回との差異を送信する機能を追加

スリープ間隔と前回との差異を送信する機能を追加

--- 2_example/example09m_hum/example09m_hum.ino
+++ 2_example/example09m_hum/example09m_hum.ino
@@ -23,11 +23,11 @@
 Ambient ambient;
 WiFiClient client;
 float temp,hum;                             // センサ用の浮動小数点数型変数
+int mem;                                    // RTCメモリからの数値データ保存用
 extern int WAKE_COUNT;

 void setup(){                               // 起動時に一度だけ実行する関数
     int waiting=0;                          // アクセスポイント接続待ち用
-    int mem;                                // RTCメモリからの数値データ保存用
     pinMode(PIN_LED,OUTPUT);                // LED用ポートを出力に
     digitalWrite(PIN_LED,HIGH);             // LEDの点灯
     hdcSetup();                             // 湿度センサの初期化
@@ -74,8 +74,13 @@
         ambient.set(1,s);                   // Ambient(データ1)へ温度を送信
         dtostrf(hum,5,2,s);                 // 湿度を文字列に変換
         ambient.set(2,s);                   // Ambient(データ2)へ湿度を送信
+        itoa(mem,s,3);                      // 前回との差異を文字列へ変換
+        ambient.set(3,s);                   // Ambient(データ3)へ差異を送信
+        itoa(WAKE_COUNT,s,2);               // 前回からの測定間隔数を文字列へ変換
+        ambient.set(4,s);                   // Ambient(データ4)へ測定間隔数を送信
         ambient.send();                     // Ambient送信の終了(実際に送信する)
     }
+    WAKE_COUNT=1;                           // 起動回数をリセット
     delay(200);                             // 送信待ち時間
     sleep();
 }


externalized the power object shift, need to implement windowing on new inserts

--- Arduino/linkit_duo_led6/linkit_duo_led6.ino
+++ Arduino/linkit_duo_led6/linkit_duo_led6.ino
@@ -486,11 +486,11 @@
     for(int i = 0; i < EASE_COUNT; i++){
       int pos = elastic_ease[i] * count;
       delay(EASE_DELAY);
-      shift(pos, max);
-    }
-    if(!display_only){
+      shift(pos+1, max);
+    }
+//    if(!display_only){
       finalize_shift(count, max);
-    }
+//    }
   }
 }

@@ -502,9 +502,9 @@
       delay(POWER_EASE_DELAY);
       shift(pos+1, max);
     }
-    if(!display_only){
+//    if(!display_only){
       finalize_shift(count, max);
-    }
+//    }
   }
 }

@@ -513,6 +513,10 @@
 #define DEMO_TOTAL_SIZE (DEMO_OBJECT_SIZE + DEMO_GAP_SIZE)

 // be able to reverse shift/animation direction
+
+void power_shift_object(int width, int shift){
+  do_power_shift(shift, shift + width);
+}

 void do_demo(int count = ANIM_LED_COUNT / DEMO_TOTAL_SIZE){

@@ -526,7 +530,12 @@

   for(int i = 0; i < count; i++){
     rgb_color color = random_color();
-    do_power_shift(window, window + DEMO_TOTAL_SIZE);
+
+//    do_power_shift(window, window + DEMO_TOTAL_SIZE);
+//    //do_elastic_shift(window, window + DEMO_TOTAL_SIZE);
+
+    power_shift_object(DEMO_TOTAL_SIZE, window);
+
     window -= DEMO_TOTAL_SIZE;

     int effect = random_effect();
@@ -567,6 +576,16 @@

 bool is_command(char *str, char *command){
   return strcmp(str, command) == 0;
+}
+
+#define NUM_SUB_ARGS 2
+int sub_args[NUM_SUB_ARGS] = { 0, 0 };
+
+int get_sub_args(char * args){
+  char *arg = strtok(args, ",");
+  sub_args[0] = atoi(arg);
+  arg = strtok(NULL, ",");
+  sub_args[1] = atoi(arg);
 }

 void loop(){
@@ -623,8 +642,8 @@
 // separate velocities per type of animation

     if(is_command(str, "repeat")){
-      int times = String(arg).toInt();
-      if(times == 0) times = 1;
+      int times = sub_args[0];
+      if(times < 1) times = 1;
       do_repeat(times);
       strcpy(arg, "");
     }
@@ -654,8 +673,9 @@
     else if(is_command(str, "flood"))    do_flood();
     else if(is_command(str, "random"))   do_random();
     else if(is_command(str, "mirror"))   do_mirror();
-    else if(is_command(str, "eshift"))   do_elastic_shift(String(arg).toInt());
-    else if(is_command(str, "pshift"))   do_power_shift(String(arg).toInt());
+    else if(is_command(str, "eshift"))   do_elastic_shift(sub_args[0]);
+    else if(is_command(str, "pshift"))   do_power_shift(sub_args[0]);
+    else if(is_command(str, "pshifto"))  power_shift_object(sub_args[0], sub_args[1]);
     else if(is_command(str, "demo"))     do_demo();
     else if(is_command(str, "static"))   start_static();
     else if(is_command(str, "red"))      push_color(red);
@@ -674,7 +694,7 @@
     else if(is_command(str, "seafoam"))  push_color(seafoam);
     else if(is_command(str, "ltblue"))   push_color(ltblue);
     else if(is_command(str, "dkgray"))   push_color(dkgray);
-    else strcpy(arg, str);
+    else strcpy(arg, str); get_sub_args(arg);

     //strcpy(last, arg);
   }


saving any changes

saving any changes

--- Arduino/linkit_duo_led6/linkit_duo_led6.ino
+++ Arduino/linkit_duo_led6/linkit_duo_led6.ino
@@ -12,7 +12,7 @@
 #define POWER_EASE_COUNT 24
 #define EASE_DELAY 2
 #define POWER_EASE_DELAY 1
-#define EASE_ANIM_MARGIN 15
+#define EASE_ANIM_MARGIN 10

 #define ANIM_LED_COUNT 64
 #define LED_COUNT (ANIM_LED_COUNT + EASE_ANIM_MARGIN)
@@ -465,11 +465,12 @@
 }

 // animate by shifting frame (future: shift in from back buffer)
-void shift(int count, int max = ANIM_LED_COUNT){
+void shift(int count, int maxx = ANIM_LED_COUNT){
+  maxx = min(maxx, LED_COUNT);
   for(int i = 0; i < count; i++){
     render[i] = black;
   }
-  for(int i = count; i < max; i++){
+  for(int i = count; i < maxx; i++){
     render[i] = scale_color(colors[i - count], DEFAULT_BRIGHTNESS_SCALE);
   }

@@ -602,6 +603,8 @@
   window = width;
 }

+#define DELIMITER_CHAR ':'
+
 void loop(){
   char str[MAX_STRING_LENGTH];
   char arg[MAX_STRING_LENGTH];
@@ -609,7 +612,7 @@
   rgb_color color;

   if(Serial1.available() > 0){
-    int c = Serial1.readBytesUntil('|', str, MAX_STRING_LENGTH);
+    int c = Serial1.readBytesUntil(DELIMITER_CHAR, str, MAX_STRING_LENGTH);
     str[c] = 0;

     // reset the effects so the automatic render won't interfere
@@ -654,6 +657,7 @@
 // need a window on animations so later parts of buffer can remain untouched
 // separate velocities per type of animation
 // reset command
+// restrict flood, etc. to window

     if(is_command(str, "repeat")){
       int times = sub_args[0];


Arduino code to enable the tide times.

Arduino code to enable the tide times.

--- BeanSurfMap_Arduino/BeanSurfMap_Arduino.ino
+++ BeanSurfMap_Arduino/BeanSurfMap_Arduino.ino
@@ -6,11 +6,11 @@
  */
 #define FORECASTDAYS 6
 #define DATA_PIN 5
-#define PIXELS_IN_STRIP 56
+#define PIXELS_IN_STRIP 88//56
 #define PIXELS_PER_ROW 8
-
-#define ON_MS 4000
-#define OFF_MS   10000
+#define PIXELS_PER_RING 16
+
+
 #define FADE_ITERATIONS  50
 #define FADE_SLEEP_MS    30
 #define FADE_INCREMENT_VALUE 2
@@ -31,7 +31,8 @@
 uint8_t brightness;
 uint8_t loopCount;
 bool receivedConditions=false;
-
+uint8_t offSeconds=10;
+uint8_t onSeconds=4;
 void setAllPixelsRGB(int r, int g, int b){
   for (int i=0; i< PIXELS_IN_STRIP; i++){
     pixels[i]=((uint32_t)r << 16) | ((uint32_t)g <<  8) | b;
@@ -40,6 +41,11 @@
 void  setAllPixels(uint32_t p){
   for (int i=0; i< PIXELS_IN_STRIP; i++){
     pixels[i]=p;
+  }
+}
+void setNPixelsAtIndex(uint8_t index, uint8_t n, uint32_t color){
+    for (int i=index; i< index+n; i++){
+    pixels[i]=color;
   }
 }

@@ -94,45 +100,93 @@
     if ( Serial.available() >= SPOT_REPORT_SIZE )
     {
       Serial.readBytes(buffer, SPOT_REPORT_SIZE);
-      lastSpotUpdated=buffer[0];
       if (buffer[1]==0x00 && buffer[2]==FORECASTDAYS){       //0 denotes wave heights
-        for (int i=0; i<FORECASTDAYS; i++){
-           waveHeights[i]=buffer[i+3];
-         }
-         for (int i=0; i<FORECASTDAYS; i++){        //surf conditions
-           waveConditions[i]=buffer[i+10];
-         }
-         receivedConditions=true;
-        for (int i=0; i<FORECASTDAYS; i++){
-          int ht=waveHeights[i]*waveHeights[i]*4;
-          if (ht>255){
-            ht=255;
-          }
-          if (waveConditions[i]<2){                 //flat or no forecast
-            strip.setPixelColor(i+lastSpotUpdated*8,0,0,10 );
-          }
-          else if (waveConditions[i]>=2 && waveConditions[i]<4){ //poor = blue. The color of the summer here in NorCal
-            pixels[i+lastSpotUpdated*8]=strip.Color(0, 0, ht);
-          }
-          else if (waveConditions[i]==4){           //poor to fair = bluegreen. Let's be optimistic here... ;)
-            pixels[i+lastSpotUpdated*8]=strip.Color(0, ht/2+20, ht);
-          }
-          else if (waveConditions[i]>=5 && waveConditions[i]<7){ //fair = green
-            pixels[i+lastSpotUpdated*8]=strip.Color(0,ht,0 );
-          }
-          else if (waveConditions[i]>=7 && waveConditions[i]<9){ //good = orange
-            pixels[i+lastSpotUpdated*8]=strip.Color(ht,ht,0 );
-          }
-          else if (waveConditions[i]>=9){           //EPIC!! Stop what you're doing and go surf!!1
-            pixels[i+lastSpotUpdated*8]=strip.Color(ht,0,00 );
-          }
-          if (i==FORECASTDAYS-1){
-            pixels[i+lastSpotUpdated*8+1]=pixels[i+lastSpotUpdated*8];    //have the next 2 pixels match the last day in the forecast
-            pixels[i+lastSpotUpdated*8+2]=pixels[i+lastSpotUpdated*8];
-          }
-      }
-      updateStripWithPixels();                      //update immediately after receiving new value.
-    }
+      lastSpotUpdated=buffer[0];
+      if (lastSpotUpdated>3 && lastSpotUpdated<8){ //skip over the ring LEDs (16 each, 32 total / 8 == 4. The tide times index is 8
+        lastSpotUpdated+=4;
+      }
+
+      for (int i=0; i<FORECASTDAYS; i++){
+         waveHeights[i]=buffer[i+3];
+       }
+       for (int i=0; i<FORECASTDAYS; i++){        //surf conditions
+         waveConditions[i]=buffer[i+10];
+       }
+       receivedConditions=true;
+      for (int i=0; i<FORECASTDAYS; i++){
+        int ht=waveHeights[i]*waveHeights[i]*4;
+        if (ht>255){
+          ht=255;
+        }
+        if (waveConditions[i]<2){                 //flat or no forecast
+          strip.setPixelColor(i+lastSpotUpdated*8,0,0,10 );
+        }
+        else if (waveConditions[i]>=2 && waveConditions[i]<4){ //poor = blue. The color of the summer here in NorCal
+          pixels[i+lastSpotUpdated*8]=strip.Color(0, 0, ht);
+        }
+        else if (waveConditions[i]==4){           //poor to fair = bluegreen. Let's be optimistic here... ;)
+          pixels[i+lastSpotUpdated*8]=strip.Color(0, ht/2+20, ht);
+        }
+        else if (waveConditions[i]>=5 && waveConditions[i]<7){ //fair = green
+          pixels[i+lastSpotUpdated*8]=strip.Color(0,ht,0 );
+        }
+        else if (waveConditions[i]>=7 && waveConditions[i]<9){ //good = orange
+          pixels[i+lastSpotUpdated*8]=strip.Color(ht,ht,0 );
+        }
+        else if (waveConditions[i]>=9){           //EPIC!! Stop what you're doing and go surf!!1
+          pixels[i+lastSpotUpdated*8]=strip.Color(ht,0,00 );
+        }
+        if (i==FORECASTDAYS-1){
+          pixels[i+lastSpotUpdated*8+1]=pixels[i+lastSpotUpdated*8];    //have the next 2 pixels match the last day in the forecast
+          pixels[i+lastSpotUpdated*8+2]=pixels[i+lastSpotUpdated*8];
+        }
+      }
+    }
+    else if (buffer[1]==0x02 ){  //tides
+      lastSpotUpdated=buffer[0];
+      if (buffer[3]>12) //if PM
+        setNPixelsAtIndex(lastSpotUpdated*8,PIXELS_PER_RING,strip.Color(0,60,0));     //dimly turn on all ring LEDs so the ring shape comes through.
+      else
+        setNPixelsAtIndex(lastSpotUpdated*8,PIXELS_PER_RING,strip.Color(0,0,80));     //dimly turn on all ring LEDs so the ring shape comes through.
+      if (buffer[5]>12) //if PM
+        setNPixelsAtIndex(lastSpotUpdated*8+PIXELS_PER_RING,PIXELS_PER_RING,strip.Color(0,60,0));     //dimly turn on all ring LEDs so the ring shape comes through.
+      else
+        setNPixelsAtIndex(lastSpotUpdated*8+PIXELS_PER_RING,PIXELS_PER_RING,strip.Color(0,0,80));     //dimly turn on all ring LEDs so the ring shape comes through.
+
+      //low tide first
+      uint8_t pixelHour,pixelMinute;
+
+      if (buffer[3]==0 && buffer[4]==0){
+        buffer[3]=1;
+        buffer[4]=20;
+        pixelHour= (buffer[3]%12)*(16.0/12.0);     //mod 12 to remove 24hr time, 16/12 to go from 12 values to 16 LEDs.
+        pixels[pixelHour+lastSpotUpdated*8]=strip.Color(200,0,0);
+        pixelMinute= buffer[4]*(16.0/60.0); //
+        pixels[pixelMinute+lastSpotUpdated*8]=strip.Color(0,200,0);
+      }
+      else{
+        pixelHour= (buffer[3]%12)*(16.0/12.0);     //mod 12 to remove 24hr time, 16/12 to go from 12 values to 16 LEDs.
+        pixels[pixelHour+lastSpotUpdated*8]=strip.Color(200,0,0);
+        pixelMinute= buffer[4]*(16.0/60.0); //
+        pixels[pixelMinute+lastSpotUpdated*8]=strip.Color(0,200,0);
+      }
+      //high tide next
+      if (buffer[5]==0 && buffer[6]==0){//no high tide
+
+      }
+      else{
+        pixelHour= (buffer[5]%12)*(16.0/12.0);    //mod 12 to remove 24hr time, 16/12 to go from 12 values to 16 LEDs.
+        pixels[pixelHour+lastSpotUpdated*8+PIXELS_PER_RING]=strip.Color(200,0,0);
+        pixelMinute= buffer[6]*(16.0/60.0); //
+        pixels[pixelMinute+lastSpotUpdated*8+PIXELS_PER_RING]=strip.Color(0,200,0);
+      }
+    }
+    else if (buffer[1]=0x03){ //set up
+      offSeconds=buffer[2];
+      onSeconds=buffer[3];
+
+    }
+    updateStripWithPixels();
    }
    //fade on
    for (int i=0;i<FADE_ITERATIONS;i++){
@@ -146,7 +200,7 @@
    brightness=FADE_INCREMENT_VALUE*FADE_ITERATIONS;
    updateStripWithBrightness(brightness);
    if (Serial.available()<SPOT_REPORT_SIZE){        //don't sleep or fade if you have another report waiting.
-     Bean.sleep(ON_MS);
+     Bean.sleep(onSeconds*1000);
    }

    //fade off
@@ -161,7 +215,7 @@
    brightness=0;
    updateStripWithBrightness(brightness);
     if (Serial.available()<SPOT_REPORT_SIZE){       //don't sleep or fade if you have another report waiting.
-     Bean.sleep(OFF_MS);
+     Bean.sleep(offSeconds*1000);
    }

 }


gotoの廃止、関数化

gotoの廃止、関数化

--- RGB_matrix.ino
+++ RGB_matrix.ino
@@ -15,26 +15,34 @@
     pinMode(11, OUTPUT);
     pinMode(12, OUTPUT);
     pinMode(13, OUTPUT);
-}

-void loop() {
-    digitalWrite(0, LOW);
-    digitalWrite(1, LOW);
-    digitalWrite(2, LOW);
-    digitalWrite(3, LOW);
-    digitalWrite(4, LOW);
+    digitalWrite( 0, LOW);
+    digitalWrite( 1, LOW);
+    digitalWrite( 2, LOW);
+    digitalWrite( 3, LOW);
+    digitalWrite( 4, LOW);
     digitalWrite(10, LOW);
     digitalWrite(11, LOW);
     digitalWrite(12, LOW);
     digitalWrite(13, LOW);
+}

-    start:
+const int MODE0=0;
+const int MODE1=1;
+const int MODE2=2;
+
+int buttonState=1;
+void loop() {
+    static int next_mode = MODE0;
+    if(next_mode==MODE0)      next_mode = start1();
+    else if(next_mode==MODE1) next_mode = music1();
+    else if(next_mode==MODE2) next_mode = music2();
+}
+
+int start1(){
     delay(500);
-    int buttonState=1;
-
-    start1:
     buttonState=digitalRead(9);
-    if (buttonState ==LOW ) {goto music1;}
+    if (buttonState ==LOW ) return MODE1;
     else{
         analogReference(INTERNAL);
         int x=analogRead(5);
@@ -51,15 +59,15 @@
         else{tone(8, 5100,100);}
         delay(1000);
     }
-    goto start1;
+    return MODE0;
+}

-
-    music1:
+int music1(){
     delay(500);
     buttonState=1;
     for (int thisNote = 0; thisNote < 82; thisNote++) {
        buttonState=digitalRead(9);
-       if (buttonState ==LOW ) {goto music2;}
+       if (buttonState ==LOW ) return MODE2;
        if(thisNote == 0){
             digitalWrite(5,LOW);
             digitalWrite(6,LOW);
@@ -150,9 +158,10 @@
         int pause = duration * 1.5; //音符の間で時間をあける
         delay(pause);
     }
-    goto music1;
+    return MODE1;
+}

-    music2:
+int music2(){
     delay(500);
     buttonState=1;
     digitalWrite(0, LOW);
@@ -167,7 +176,7 @@

     for (int thisNote = 0; thisNote < 68; thisNote++) {
         buttonState=digitalRead(9);
-        if (buttonState ==LOW ) {goto start;}
+        if (buttonState ==LOW ) return MODE0;
         digitalWrite(5,HIGH);
         digitalWrite(6,HIGH);
         digitalWrite(7,HIGH);
@@ -180,6 +189,6 @@
         int pause = duration * 1.5;  //音符の間で時間をあける

         delay(pause);
-    }
-    goto music2;
-}
+    }
+    return MODE2;
+}


Update ReceiveInterrupt.ino

Update ReceiveInterrupt.ino

--- examples/ReceiveInterrupt/ReceiveInterrupt.ino
+++ examples/ReceiveInterrupt/ReceiveInterrupt.ino
@@ -125,7 +125,7 @@
     // you can also read received data as byte array
     /*
       byte byteArr[8];
-      int state = lora.receive(byteArr, 8);
+      int state = lora.readData(byteArr, 8);
     */

     if (state == ERR_NONE) {


changed laser ref

changed laser ref


Update version_info.ino

Update version_info.ino

--- Arduino/McLighting/version_info.ino
+++ Arduino/McLighting/version_info.ino
@@ -81,4 +81,5 @@
  *
  * 7 Mar 2019 v 2.2.2
  * - Add compiler flag for WS2811 strips #define LED_TYPE_WS2811
+ * - Hotfix #351
  */


Clean up functions

Clean up functions

--- module/module.ino
+++ module/module.ino
@@ -31,13 +31,14 @@
 }

 void loop() {
+	// some variables
 	bool responseFlag = false;
 	unsigned int userID = 0;
 	unsigned long existingTime, elapsedTime = 0;
 	unsigned char readData[bufferSize];
 	unsigned long lastSend = millis();
 	digitalWrite(ledPin, LOW);
-
+	// ----------------------------------------------------------------------
 	// Scan for RFID tags
 	Serial.print(messages.scan);
 	while (!responseFlag) {
@@ -46,7 +47,7 @@
 		delay(waitforSerialResponse);
 		responseFlag = getResponse(readData);
 	}
-
+	// -----------------------------------------------------------------------
 	// RFID tag detected, read block that contains time data (for this machine)
 	// LED will turn on
 	Serial.print(messages.detected);
@@ -54,7 +55,7 @@
 	sendCommand(CMD_READ, blockID, machineID, keyA, NULL);
 	delay(waitforReadResponse);
 	responseFlag = getResponse(readData);
-
+	// ------------------------------------------------------------------------
 	// Analyze response packet and data
 	if (!responseFlag) {
 		soundFeedback(reject);
@@ -75,6 +76,7 @@
 			Serial.print(messages.quotaMet);
 		}
 		// User passed all checks and is able to use the machine
+		// ---------------------------------------------------------------------
 		else {
 			// Get user ID
 			sendCommand(CMD_READ, blockID, userData, keyA, NULL);
@@ -94,8 +96,7 @@
 			Serial.println(elapsedTime + existingTime);
 		}
 	}
-
-
+	// -------------------------------------------------------------------------
 	// Write time data to card
 	if (elapsedTime > 0) {
 		sendCommand(CMD_WRITE, blockID, machineID, keyA, elapsedTime + existingTime);
@@ -111,7 +112,7 @@
 			Serial.print(messages.cardUpdated);
       		WIFI.listen();
 			delay(timeToRemoveCard);
-
+			// -----------------------------------------------------------------
       		// Push to ThingSpeak
 			Serial.print(messages.sendingLog);
 			Serial.println(elapsedTime);
@@ -121,7 +122,8 @@
 			}
 			updateThingSpeak(userID, elapsedTime);
 			lastSend = millis();
-
+			// -----------------------------------------------------------------
+			// Finished, prepare for next loop by switching to RFID serial port
 			Serial.println(messages.done);
 		}
 	}
@@ -134,8 +136,11 @@

 }
 /*
-	Function that waits for a control signal to go high and counts time
-	that control signal is high for.
+	This function monitors the motor driver signals on the laser cutter.
+	The signals are PWMs of a specific frequency. This function measures the
+	half-period of any signal detected and accumulates time when valid signals
+	are detected. A polling subroutine continuously checks if an RFID tag is
+	still present.

 	Return: time accumulated

@@ -148,6 +153,7 @@
 	unsigned int pulseCount, sendCount = 0;
 	unsigned int pollCounter = 0;

+	// Only here temporarily
 	if (debug) {
 		Serial.print(startTime);
 		Serial.print(" ");
@@ -187,6 +193,7 @@
 		periodX = pulseIn(driverX, HIGH);
 		periodY = pulseIn(driverY, HIGH);

+		// Only here temporarily
 		if (debug) {
 			Serial.print("PeriodX: ");
 			Serial.print(periodX);
@@ -216,7 +223,7 @@
 			if ( inRange(lastPeriodX, lastPeriodY) ) {
 				// check if the job was more than 5 seconds
 				if (pulseCount > minCount) {
-					// calculate elapsed time
+					// calculate elapsed time in seconds
 					return (millis() - startTime)/1000;
 				}
 				pulseCount = 0;
@@ -231,7 +238,12 @@
 	}
 }
 /*
-	Determines if the driver signals are valid
+	Determines if the driver signals are valid. During a job,
+	the half-period of a pulse is either 0, 5 or 6 microseconds.
+	There are slight deviations at times, so summing the values together
+	and using the maximum possible sum as a check is a reliable way to
+	determine if a signal is valid.
+
 */
 bool inRange(unsigned long periodX, unsigned long periodY) {
 	unsigned long sum = periodX + periodY;
@@ -240,7 +252,7 @@
 	if (sum == 0) {
 		return true;
 	}
-	// both sum to less than 12
+	// both sum to less than or equal to 12
 	else if (sum <= maximumValue) {
 		return true;
 	}


QLink fix

QLink fix

--- MicroView/WiFiModem-Menu/WiFiModem-Menu.ino
+++ MicroView/WiFiModem-Menu/WiFiModem-Menu.ino
@@ -46,7 +46,7 @@

 ;  // Keep this here to pacify the Arduino pre-processor

-#define VERSION "0.13b1"
+#define VERSION "0.13b2"

 unsigned int BAUD_RATE=2400;
 unsigned int WiFly_BAUD_RATE=2400;
@@ -1062,13 +1062,13 @@

 void Connect(String host, int port, boolean raw)
 {
-    bool isQLink = false;
-
-    if ( (host == F("5551212")) || (host == F("5551213")) || (host == F("QLINK")) )    // Note case!
-    {
+    if (host == F("5551212")) {
+        host = F("qlink.lyonlabs.org");
+        port = 5190;
+    }
+    else if (host == F("5551213") || (host == F("QLINK"))) {
         host = F("q-link.net");
         port = 5190;
-        isQLink = true;   // Workaround Telnet detection
     }
 #ifdef HAYES
     else if (host == F("CS38")) {
@@ -1160,11 +1160,6 @@

 #ifdef HAYES
     Modem_Connected(false);
-
-    if (isQLink)
-    {
-        isFirstChar = false;  // force off
-    }
 #else
     /*if (!raw)
     {
@@ -1199,44 +1194,39 @@
                 if (data == NVT_IAC)
                 {
                     isTelnet = true;
-                    CheckTelnetInline();
                 }
                 else
                 {
                     isTelnet = false;
                 }
-                isFirstChar = false;
+            }
+
+            if (data == NVT_IAC && isTelnet)
+            {
+                if (baudMismatch)
+                {
+                    digitalWriteFast(WIFI_CTS, LOW);        // re-enable data
+                    if (CheckTelnetInline())
+                        buffer[buffer_index++] = NVT_IAC;
+                    digitalWriteFast(WIFI_CTS, HIGH);     // ..stop data from Wi-Fi
+                }
+                else
+                {
+                    if (CheckTelnetInline())
+                        C64Serial.write(NVT_IAC);
+                }
             }
             else
             {
-                if (data == NVT_IAC && isTelnet)
+                if (baudMismatch)
                 {
-                    if (baudMismatch)
-                    {
-                        digitalWriteFast(WIFI_CTS, LOW);        // re-enable data
-                        if (CheckTelnetInline())
-                            buffer[buffer_index++] = NVT_IAC;
-                        digitalWriteFast(WIFI_CTS, HIGH);     // ..stop data from Wi-Fi
-                    }
-                    else
-                    {
-                        if (CheckTelnetInline())
-                            C64Serial.write(NVT_IAC);
-                    }
-
+                    digitalWriteFast(WIFI_CTS, HIGH);     // ..stop data from Wi-Fi
+                    buffer[buffer_index++] = data;
                 }
                 else
                 {
-                    if (baudMismatch)
-                    {
-                        digitalWriteFast(WIFI_CTS, HIGH);     // ..stop data from Wi-Fi
-                        buffer[buffer_index++] = data;
-                    }
-                    else
-                    {
-                        DoFlowControlModemToC64();
-                        C64Serial.write(data);
-                    }
+                    DoFlowControlModemToC64();
+                    C64Serial.write(data);
                 }
             }
         }
@@ -1357,8 +1347,10 @@
     int inpint, verbint, optint;                        //    telnet parameters as integers

     // First time through
-    if (isFirstChar)
+    if (isFirstChar) {
         SendTelnetParameters();                         // Start off with negotiating
+        isFirstChar = false;
+    }

     verbint = ReadByte(wifly);                          // receive negotiation verb character

@@ -1868,7 +1860,7 @@
             case 'Z':   // ATZ
                 Modem_LoadSavedSettings();
                 if (wifly.isSleeping())
-                    wake();
+                    mWake();
                 break;

             case 'I':   // ATI
@@ -1917,7 +1909,7 @@
                 {
                 case '0':
                     if (wifly.isSleeping())
-                        wake();
+                        mWake();
                     else
                         Modem_Disconnect(false);
                     break;
@@ -1931,7 +1923,7 @@
                 default:
                     i--;                        // User entered ATH
                     if (wifly.isSleeping())
-                        wake();
+                        mWake();
                     else
                         Modem_Disconnect(false);
                     break;
@@ -2111,7 +2103,7 @@
                 case 'F':   // AT&F
                     Modem_LoadDefaults(false);
                     if (wifly.isSleeping())
-                        wake();
+                        mWake();

                     break;

@@ -2492,56 +2484,51 @@
                     {
                         while (wifly.available() > 0)
                         {
-                            int data = wifly.read();
+                            int data = wifly.read();

                             // If first character back from remote side is NVT_IAC, we have a telnet connection.
-                            if (isFirstChar)
-                            {
+                            if (isFirstChar) {
                                 if (data == NVT_IAC)
                                 {
                                     isTelnet = true;
-                                    CheckTelnetInline();
                                 }
                                 else
                                 {
                                     isTelnet = false;
                                 }
-                                isFirstChar = false;
+                            }
+
+                            if (data == NVT_IAC && isTelnet)
+                            {
+                                if (baudMismatch)
+                                {
+                                    digitalWriteFast(WIFI_CTS, LOW);        // re-enable data
+                                    if (CheckTelnetInline())
+                                        buffer[buffer_index++] = NVT_IAC;
+                                    digitalWriteFast(WIFI_CTS, HIGH);     // ..stop data from Wi-Fi
+                                }
+                                else
+                                {
+                                    if (CheckTelnetInline())
+                                        C64Serial.write(NVT_IAC);
+                                }
                             }
                             else
                             {
-                                  if (data == NVT_IAC && isTelnet)
-                                  {
-                                      if (baudMismatch)
-                                      {
-                                        digitalWriteFast(WIFI_CTS, LOW);        // re-enable data
-                                        if (CheckTelnetInline())
-                                            buffer[buffer_index++] = NVT_IAC;
-                                        digitalWriteFast(WIFI_CTS, HIGH);     // ..stop data from Wi-Fi
-                                    }
-                                    else
-                                    {
-                                        if (CheckTelnetInline())
-                                            C64Serial.write(NVT_IAC);
-                                    }
-
+                                if (baudMismatch)
+                                {
+                                    digitalWriteFast(WIFI_CTS, HIGH);     // ..stop data from Wi-Fi
+                                    buffer[buffer_index++] = data;
                                 }
                                 else
                                 {
-                                    if (baudMismatch)
-                                    {
-                                        digitalWriteFast(WIFI_CTS, HIGH);     // ..stop data from Wi-Fi
-                                        buffer[buffer_index++] = data;
-                                    }
-                                    else
-                                    {
-                                        DoFlowControlModemToC64();
-                                        C64Serial.write(data);
-                                    }
-                               }
+                                    DoFlowControlModemToC64();
+                                    C64Serial.write(data);
+                                }
                             }
+
+
                         }
-
                         if (baudMismatch)
                         {
                             // Dump the buffer to the C64 before clearing WIFI_CTS
@@ -3034,7 +3021,7 @@

 }

-void wake()
+void mWake()
 {
     setBaudWiFi2(2400);
     wifly.wake();


Only start moonSimtask if enabled in setup.

--- ntptask.ino
+++ ntptask.ino
@@ -58,15 +58,17 @@
     ESP_LOGI( TAG, "LoggerTask %s.", ( xReturned == pdPASS ) ? "started" : "failed" );
   }

-  xReturned = xTaskCreatePinnedToCore(
-                moonSimtask,                    /* Function to implement the task */
-                "moonSimtask",                  /* Name of the task */
-                2000,                           /* Stack size in words */
-                NULL,                           /* Task input parameter */
-                moonSimtaskPriority,            /* Priority of the task */
-                NULL,                           /* Task handle. */
-                1);                             /* Core where the task should run */
-
+  if ( MOON_SIMULATOR )
+  {
+    xReturned = xTaskCreatePinnedToCore(
+                  moonSimtask,                    /* Function to implement the task */
+                  "moonSimtask",                  /* Name of the task */
+                  2000,                           /* Stack size in words */
+                  NULL,                           /* Task input parameter */
+                  moonSimtaskPriority,            /* Priority of the task */
+                  NULL,                           /* Task handle. */
+                  1);                             /* Core where the task should run */
+  }
   ESP_LOGI( TAG, "moonSimtask %s.", ( xReturned == pdPASS ) ? "started" : "failed" );

   vTaskDelete( NULL );


Fix math bug on 16-bit processors

Fix math bug on 16-bit processors

When scaling analog input values, cast the state variable to uint32_t
to avoid truncating the multiplication.  This avoids problems on
processors where an int is smaller than 32 bits.

--- MidiController.ino
+++ MidiController.ino
@@ -276,7 +276,8 @@
       else if (state > p->Max) {
         state = p->Max;
       }
-      state = 1023 * (state - p->Min) / (p->Max - p->Min);
+      // use a wide enough integer for the intermediate values
+      state = (uint32_t)1023 * (state - p->Min) / (p->Max - p->Min);
       break;
     case Digital:
     case DigitalPullup:


fix arduino

fix arduino

--- arduino/arduino.ino
+++ arduino/arduino.ino
@@ -4,9 +4,6 @@
 #define PIN_LED 13
 #define PIN_IR_IN 3
 #define PIN_IR_OUT 12
-#define READ 0
-#define WRITE 1
-byte mode;

 void setup(){
   Serial.begin(57600);


Delete sensorflare-pwm.ino

Delete sensorflare-pwm.ino


TODO-721: WIP

TODO-721: WIP

--- test_SECFRAME/test_SECFRAME.ino
+++ test_SECFRAME/test_SECFRAME.ino
@@ -878,6 +878,18 @@
   AssertIsTrue((0 != mcbuf[3]) || (0 != mcbuf[4]) || (0 != mcbuf[5]));
   }

+// Test some basic parameters of node associations.
+// Tests to only be run once because they may cause device wear.
+// NOTE: best not to run on a real device as this will mess with its associations, etc.
+static void testNodeAssocRunOnce()
+  {
+  Serial.println("NodeAssocRunOnce");
+  OTV0P2BASE::clearAllNodeAssociations();
+  AssertIsEqual(0, OTV0P2BASE::countNodeAssociations());
+  AssertIsEqual(-1, OTV0P2BASE::getNextMatchingNodeID(0, NULL, 0, NULL));
+  // TODO: test adding associations and looking them up.
+  }
+

 // To be called from loop() instead of main code when running unit tests.
 // Tests generally flag an error and stop the test cycle with a call to panic() or error().
@@ -923,6 +935,7 @@
     runOnce = true;
     Serial.println(F("Run-once tests... "));
     testPermMsgCountRunOnce();
+    testNodeAssocRunOnce();
     }


TODO-721: compiles and runs and with new test

--- test_SECFRAME/test_SECFRAME.ino
+++ test_SECFRAME/test_SECFRAME.ino
@@ -791,9 +791,6 @@
   Serial.println("PermMsgCount");
   uint8_t loadBuf[OTV0P2BASE::VOP2BASE_EE_LEN_PERSISTENT_MSG_RESTART_CTR];
   uint8_t buf[OTRadioLink::SimpleSecureFrame32or0BodyBase::primaryPeristentTXMessageRestartCounterBytes];
-//  // Initial test that blank EEPROM after processing yields all zeros.
-//  OTRadioLink::loadRaw3BytePersistentTXRestartCounterFromEEPROM(buf);
-//  for(int i = 0; i < sizeof(buf); ++i) { AssertIsEqual(0, buf[i]); }
   // Initialise to state of empty EEPROM; result should be a valid all-zeros restart count.
   memset(loadBuf, 0, sizeof(loadBuf));
   AssertIsTrue(OTRadioLink::SimpleSecureFrame32or0BodyV0p2::read3BytePersistentTXRestartCounter(loadBuf, buf));
@@ -820,6 +817,9 @@
     AssertIsEqual(i, buf[2]);
     AssertIsTrue(OTRadioLink::SimpleSecureFrame32or0BodyV0p2::getInstance().increment3BytePersistentTXRestartCounter(loadBuf));
     }
+  // Also verify in passing that all zero message counter will never be acceptable for an RX message,
+  // regardless of the node ID, since the new count as to be higher than any previous for the ID.
+  AssertIsTrue(!OTRadioLink::SimpleSecureFrame32or0BodyV0p2::getInstance().validateRXMessageCount(zeroKey, zeroKey));
   }

 // Test handling of persistent/reboot/restart part of primary message counter.


Support push buttons and relays to change board status

--- particle-gui/firmware/setup.ino
+++ particle-gui/firmware/setup.ino
@@ -50,6 +50,20 @@
          },
          "mydevices": true
      }
+
+     {
+         "event": "changeSocketStatus",
+         "url": "http://api.smartpowersocket.xyz/changeSocketStatus",
+         "requestType": "POST",
+         "headers": {
+             "Content-Type": "application/json"
+         },
+         "json": {
+             "socketNum": "{{socketNum}}",
+             "apiKey": "{{apiKey}}"
+         },
+         "mydevices": true
+     }
 */
 /*  WEBHOOK SETUP - END */

@@ -108,8 +122,9 @@
     Serial.begin(9600);

     // Subscribe to the webhook response event
-    Particle.subscribe("hook-response/sendSocketInformation", photonRequestReturn , MY_DEVICES);
-    Particle.subscribe("hook-response/getServerInformation", serverRequestReturn , MY_DEVICES);
+    Particle.subscribe("hook-response/sendSocketInformation", sendSocketInformationReturn , MY_DEVICES);
+    Particle.subscribe("hook-response/getServerInformation", getServerInformationReturn , MY_DEVICES);
+    Particle.subscribe("hook-response/changeSocketStatus", changeSocketStatusReturn , MY_DEVICES);

     /* VOLTAGE SENSOR PIN */
     pinMode(voltage_pin, INPUT);     // Input Pin for the voltage sensor
@@ -283,9 +298,9 @@
 //############################

 /* CALLBACKS FOR BOTH PHOTON REQUESTS (SEND DATA, GET DATA)  - START  */
-void photonRequestReturn(const char *event, const char *data) { }
-
-void serverRequestReturn(const char *event, const char *data)
+void sendSocketInformationReturn(const char *event, const char *data) { }
+
+void getServerInformationReturn(const char *event, const char *data)
 {

     if (data) {
@@ -311,6 +326,25 @@
         }
     }
 }
+
+void changeSocketStatusReturn(const char *event, const char *data)
+{
+    if (data) {
+        if (strstr(data, "Socket1") != NULL) {
+            if (strstr(data, "Active") != NULL) {
+                statuses[0] = String("Active");
+            } else if (strstr(data, "Inactive") != NULL) {
+                statuses[0] = String("Inactive");
+            }
+        } else if (strstr(data, "Socket2") != NULL) {
+            if (strstr(data, "Active") != NULL) {
+                statuses[1] = String("Active");
+            } else if (strstr(data, "Inactive") != NULL) {
+                statuses[1] = String("Inactive");
+            }
+        }
+    }
+}
 /* CALLBACKS FOR BOTH PHOTON REQUESTS (SEND DATA, GET DATA)  - END  */

@@ -390,7 +424,14 @@
     /* Function to toggle_relay1 when the interruption from the falling edge signal from pushbutton1 happens */
     if(digitalRead(pushbutton1) == LOW && digitalRead(pushbutton2) == HIGH){
         state_relay1 = !state_relay1;
-        digitalWrite(relay1, state_relay1);       //Writes the state_relay1 to relay 1
+        digitalWrite(relay1, state_relay1);  //Writes the state_relay1 to relay 1
+
+        // Send auth data
+        String jsonChangeSocketStatus = String(
+          "{ \"apiKey\": \"" + String("{{{apiKey}}}") + "\"" +
+           ",\"socketNum\": \"" + String(1) +  "\"}" );
+
+        Particle.publish("changeSocketStatus", jsonChangeSocketStatus, PRIVATE);
     }
 }

@@ -400,7 +441,14 @@
     /* Function to toggle_relay2 when the interruption from the falling edge signal from pushbutton2 happens */
     if(digitalRead(pushbutton2) == LOW && digitalRead(pushbutton1) == HIGH){
         state_relay2 = !state_relay2;
-        digitalWrite(relay2, state_relay2);       //Writes the state_relay2 to relay 2
+        digitalWrite(relay2, state_relay2);     //Writes the state_relay2 to relay 2
+
+        // Send auth data
+        String jsonChangeSocketStatus = String(
+          "{ \"apiKey\": \"" + String("{{{apiKey}}}") + "\"" +
+           ",\"socketNum\": \"" + String(2) +  "\"}" );
+
+        Particle.publish("changeSocketStatus", jsonChangeSocketStatus, PRIVATE);
     }
 }


Structure

Structure

--- Flex_Sensor.ino/Flex_Sensor.ino.ino
+++ Flex_Sensor.ino/Flex_Sensor.ino.ino
@@ -1,107 +0,0 @@
-/******************************************************************************
-Flex_Sensor_Example.ino
-Example sketch for SparkFun's flex sensors
-  (https://www.sparkfun.com/products/10264)
-Jim Lindblom @ SparkFun Electronics
-April 28, 2016
-
-Create a voltage divider circuit combining a flex sensor with a 47k resistor.
-- The resistor should connect from A0 to GND.
-- The flex sensor should connect from A0 to 3.3V
-As the resistance of the flex sensor increases (meaning it's being bent), the
-voltage at A0 should decrease.
-
-Development environment specifics:
-Arduino 1.6.7
-******************************************************************************/
-// Include BLE files.
-#include <CurieBLE.h>
-
-//###BLE Characteristics###
-BLEPeripheral blePeripheral;       // BLE Peripheral Device (the board you're programming)
-BLEService batteryService("180F"); // BLE Battery Service
-
-// BLE Battery Level Characteristic
-BLEUnsignedCharCharacteristic batteryLevelChar("2A19",  // standard 16-bit characteristic UUID
-    BLERead | BLENotify);     // remote clients will be able to get notifications if this characteristic changes
-
-//###Flex sensor properties###
-const int FLEX_PIN = A0; // Pin connected to voltage divider output
-
-// Measure the voltage at 5V and the actual resistance of your 47k resistor, and enter them below:
-const float VCC = 4.98;       // Measured voltage of Ardunio 5V line
-const float R_DIV = 47500.0;  // Measured resistance of 3.3k resistor
-
-// Following can be adjusted to be more precise
-const float STRAIGHT_RESISTANCE = 37300.0; // resistance when straight
-const float BEND_RESISTANCE = 90000.0; // resistance at 90 deg
-
-float oldFlexAngle = 0;  // last flex angle reading from analog input
-long previousMillis = 0;  // last time the flex angle level was checked, in ms
-
-void setup()
-{
-  Serial.begin(9600);
-  pinMode(FLEX_PIN, INPUT);
-
-  /* Set a local name for the BLE device
-     This name will appear in advertising packets
-     and can be used by remote devices to identify this BLE device
-     The name can be changed but maybe be truncated based on space left in advertisement packet */
-  blePeripheral.setLocalName("ReRide");
-  blePeripheral.setAdvertisedServiceUuid(batteryService.uuid());  // add the service UUID
-  blePeripheral.addAttribute(batteryService);   // Add the BLE Battery service
-  blePeripheral.addAttribute(batteryLevelChar); // add the battery level characteristic
-  batteryLevelChar.setValue(oldFlexAngle);   // initial value for this characteristic
-
-  /* Now activate the BLE device.  It will start continuously transmitting BLE
-     advertising packets and will be visible to remote BLE central devices
-     until it receives a new connection */
-  blePeripheral.begin();
-  Serial.println("Bluetooth device active, waiting for connections...");
-}
-
-
-// This function is called continuously, after setup() completes.
-void loop()
-{
-  // listen for BLE peripherals to connect:
-  BLECentral central = blePeripheral.central();
-
-  // if a central is connected to peripheral:
-  if (central) {
-    Serial.print("Connected to central: ");
-    // print the central's MAC address:
-    Serial.println(central.address());
-    // check the flex angle every 200ms as long as the central is still connected:
-    while (central.connected()) {
-      long currentMillis = millis();
-      // if 200ms have passed, check the flex angle:
-      if (currentMillis - previousMillis >= 200) {
-        previousMillis = currentMillis;
-        updateFlexAngle();
-      }
-    }
-    Serial.print("Disconnected from central: ");
-    Serial.println(central.address());
-  }
-}
-
-void updateFlexAngle() {
-  // Read the ADC, and calculate voltage and resistance from it
-  int flexADC = analogRead(FLEX_PIN);
-  float flexV = flexADC * VCC / 1023.0;
-  float flexR = R_DIV * (VCC / flexV - 1.0);
-
-  // Use the calculated resistance to estimate the sensor's bend angle:
-  float angle = map(flexR, STRAIGHT_RESISTANCE, BEND_RESISTANCE, 0, 90.0);
-
-  if (angle != oldFlexAngle) {
-    Serial.println("Resistance: " + String(flexR) + " ohms");
-    Serial.println("Bend: " + String(angle) + " degrees");
-    Serial.println();
-    batteryLevelChar.setValue(angle);
-    oldFlexAngle = angle;
-  }
-}
-


First 'toast' implementation

First 'toast' implementation

--- HIDVolumeKnobV2/HIDVolumeKnobV2.ino
+++ HIDVolumeKnobV2/HIDVolumeKnobV2.ino
@@ -12,9 +12,14 @@
 static const unsigned int encoder0PinB  = 1;
 static const unsigned int encoderButton = 7;

+// Toast management
+static const unsigned int toastDuration = 1500;
+unsigned long lastToastTime = 0;
+
 volatile unsigned int encoder0Pos = 0;  /**< @brief Rotary encore position */
 volatile unsigned int key = 0;          /**< @brief Key code to be sent */
 volatile bool keySent = false;          /**< @brief Key send state */
+volatile bool clearAndUpdate = true;    /**< @brief Used in loop to tells if the screen should be cleared and updated */

 // Debounce parameters (expressed in ms)
 unsigned long lastDebounceTime = 0;     /**< @brief The last time rotary encoder switch button was toggled */
@@ -69,40 +74,40 @@
     display.display();
 }

+void showToastInfo(const char* string) {
+    display.clearDisplay();
+    display.setCursor(30, 15);
+    display.println(string);
+    display.display();
+    clearAndUpdate = false;
+}

 void loop() {
-    display.clearDisplay();
+    // Toast time elapsed
+    unsigned long currentTime = millis();
+    if ((currentTime - lastToastTime) > toastDuration) {
+        lastToastTime = millis();
+        clearAndUpdate = true;
+    }

-    if (!isMediaPlayerDetected) {
+    if (!isMediaPlayerDetected && clearAndUpdate) {
+        display.clearDisplay();
         display.setCursor(0, 0);
         display.println("No media player");
         display.println("detected");
+        display.display();
     }
-
-    display.display();

     if (!keySent) {
         Consumer.write(key);
         keySent = true;

-        if (key == MEDIA_PLAY_PAUSE) {
-            display.clearDisplay();
-            display.setCursor(10, 10);
-            display.println("PLAY/PAUSE");
-            display.display();
-        }
-        else if (key == MEDIA_VOLUME_UP) {
-            display.clearDisplay();
-            display.setCursor(10, 10);
-            display.println("VOLUME UP");
-            display.display();
-        }
-        else if (key == MEDIA_VOLUME_DOWN) {
-            display.clearDisplay();
-            display.setCursor(10, 10);
-            display.println("VOLUME DOWN");
-            display.display();
-        }
+        if (key == MEDIA_PLAY_PAUSE)
+            showToastInfo("PLAY/PAUSE");
+        else if (key == MEDIA_VOLUME_UP)
+            showToastInfo("VOLUME UP");
+        else if (key == MEDIA_VOLUME_DOWN)
+            showToastInfo("VOLUME DOWN");
     }
     delay(50);
 }


begin support for repeating vs one-time animations

--- nodes/pixel_node/pixel_node.ino
+++ nodes/pixel_node/pixel_node.ino
@@ -26,9 +26,9 @@
 #define LED_TYPE APA102
 #define DATA_PIN 5
 #define CLOCK_PIN 4
-#define NUM_LEDS 100
+#define NUM_LEDS 92
 #define COLOR_ORDER BGR
-#define BRIGHTNESS 50
+#define BRIGHTNESS 150

 // ------> CONFIGURE THESE VARIABLES TO MATCH YOUR SETUP  <------
 char *hostname = "arch1";                   // The hostname of this device -- eg. thishost.local
@@ -46,7 +46,7 @@
 // Some base delay times
 uint16_t loopDelay = 10;     // Time (ms) between calls to animation function
 uint16_t holdTime = 50;      // Time (ms) delay at the end of some back-n-forth functions (e.g. bounce)
-
+bool repeat = true;          // Repeat the animations. If false, they complete once then stop

 // XXXXXXXXXX  DON'T CHANGE ANYTHING BELOW THIS IN THIS FILE  XXXXXXXXXX

@@ -62,7 +62,7 @@
 boolean connectioWasAlive = true;

 char valid_colors[] = "white snow silver gray grey darkgray darkgrey black red crimson darkmagenta darkred magenta maroon orange orangered darkorange yellow gold green lime darkgreen forestgreen cyan darkcyan blue deepskyblue royalblue skyblue darkblue navy blueviolet purple violet indigo darkviolet";
-char valid_functions[] = "solid_color center_out edges_in slinky slinky_backwards bounce";
+char valid_functions[] = "solid_color center_out edges_in slinky slinky_backwards bounce bounce_backwards circle circle_backwards";
 char delim[] = ":";
 char *current_color = "black";
 std::function<void(void)> currentAnimation;
@@ -78,6 +78,7 @@
 char *current_function = "solid_color";
 uint16_t origLoopDelay = 10;
 uint16_t origHoldTime = 50;
+bool doneTheNonRepeatingAnimationOnce = false;

 //
 // Lookup table of colors - to convert string to CRGB reference
@@ -147,6 +148,15 @@
   } else if (strcmp(theFunction, "bounce") == 0) {
     currentAnimation = bounce;
     // log("currentAnimate = bounce");
+  } else if (strcmp(theFunction, "bounce_backwards") == 0) {
+    currentAnimation = bounce_backwards;
+    // log("currentAnimate = bounce_backwards");
+  } else if (strcmp(theFunction, "circle") == 0) {
+    currentAnimation = circle;
+    // log("currentAnimate = circle");
+  } else if (strcmp(theFunction, "circle_backwards") == 0) {
+    currentAnimation = circle_backwards;
+    // log("currentAnimate = circle_backwards");
   } else {
     // log("no matching function name found");
   }
@@ -169,145 +179,288 @@
 // LED pattern functions
 //
 void solid_color() {
-  // log("solid_color()");
   FastLED.showColor(currentColor);
 }

 void center_out() {
-  // log("center_out()");
-  for (int i = 0; i < NUM_LEDS; i++) {
-    if (i <= NUM_LEDS/2+edgesCenterPosition && i >= NUM_LEDS/2-edgesCenterPosition) {
-      leds[i] = currentColor;
-    } else {
-      leds[i] = CRGB::Black;
-    }
-  }
-  FastLED.show();
-
-  edgesCenterPosition = edgesCenterPosition + delta;
-
-  if (edgesCenterPosition > NUM_LEDS/2+1) {
-    // log("Reverse ... towards pos 0");
-    edgesCenterPosition -= 1;
+  if (!repeat && doneTheNonRepeatingAnimationOnce == true) {
+    edgesCenterPosition = 0;
+    return;
+  }
+  doneTheNonRepeatingAnimationOnce = true;
+  for (int j=0;j<NUM_LEDS;j++) {
+    for (int i = 0; i < NUM_LEDS; i++) {
+      if (i <= NUM_LEDS/2+edgesCenterPosition && i >= NUM_LEDS/2-edgesCenterPosition) {
+        leds[i] = currentColor;
+      } else {
+        leds[i] = CRGB::Black;
+      }
+    }
+    FastLED.show();
+
+    edgesCenterPosition = edgesCenterPosition + delta;
+
+    if (edgesCenterPosition > NUM_LEDS/2+1) {
+      // log("Reverse ... towards pos 0");
+      edgesCenterPosition -= 1;
+      delta = -1;
+      delay(holdTime);
+    } else if (edgesCenterPosition < -1) {
+      // log("Forward we go!");
+      edgesCenterPosition += 1;
+      delta = 1;
+      delay(holdTime);
+    }
+    delay(loopDelay);
+  }
+}
+
+void edges_in() {
+  if (!repeat && doneTheNonRepeatingAnimationOnce == true) {
+    edgesCenterPosition = 0;
+    return;
+  }
+  doneTheNonRepeatingAnimationOnce = true;
+  for (int j=0;j<NUM_LEDS;j++) {
+    for (int i = 0; i < NUM_LEDS; i++) {
+      if (i <= edgesCenterPosition || i>=NUM_LEDS-(edgesCenterPosition+1)) {
+        leds[i] = currentColor;
+      } else {
+        leds[i] = CRGB::Black;
+      }
+    }
+    FastLED.show();
+
+    edgesCenterPosition = edgesCenterPosition + delta;
+
+    if (edgesCenterPosition > NUM_LEDS/2+1) {
+      // log("Reverse ... towards pos 0");
+      edgesCenterPosition -= 1;
+      delta = -1;
+      delay(holdTime);
+    } else if (edgesCenterPosition < -1) {
+      // log("Forward we go!");
+      edgesCenterPosition += 1;
+      delta = 1;
+      delay(holdTime);
+    }
+    delay(loopDelay);
+  }
+}
+
+
+void slinky_backwards() {
+  if (!repeat && doneTheNonRepeatingAnimationOnce == true) {
+    slinkyPosition = NUM_LEDS;
     delta = -1;
-    delay(holdTime);
-  } else if (edgesCenterPosition < -1) {
-    // log("Forward we go!");
-    edgesCenterPosition += 1;
+    return;
+  }
+  doneTheNonRepeatingAnimationOnce = true;
+  for (int j=0;j<NUM_LEDS;j++) {
+    for (int i = 0; i < NUM_LEDS; i++) {
+      if (i > slinkyPosition) {
+        leds[i] = currentColor;
+      } else {
+        leds[i] = CRGB::Black;
+      }
+    }
+    FastLED.show();
+
+    if (slinkyPosition == NUM_LEDS - 1) {
+      // log("Reverse ... towards pos 0");
+      delta = -1;
+      delay(holdTime);
+    } else if (slinkyPosition == 0) {
+      // log("Forward we go!");
+      delta = 1;
+      delay(holdTime);
+    }
+    slinkyPosition = (slinkyPosition + delta + NUM_LEDS) % NUM_LEDS;
+    delay(loopDelay);
+  }
+}
+
+void slinky() {
+  if (!repeat && doneTheNonRepeatingAnimationOnce == true) {
+    slinkyPosition = 1;
     delta = 1;
-    delay(holdTime);
-  }
-}
-
-void edges_in() {
-  for (int i = 0; i < NUM_LEDS; i++) {
-    if (i <= edgesCenterPosition || i>=NUM_LEDS-(edgesCenterPosition+1)) {
-      leds[i] = currentColor;
-    } else {
-      leds[i] = CRGB::Black;
-    }
-  }
-  FastLED.show();
-
-  edgesCenterPosition = edgesCenterPosition + delta;
-
-  if (edgesCenterPosition > NUM_LEDS/2+1) {
-    // log("Reverse ... towards pos 0");
-    edgesCenterPosition -= 1;
+    return;
+  }
+  doneTheNonRepeatingAnimationOnce = true;
+  for (int j=0;j<NUM_LEDS;j++) {
+    for (int i = 0; i < NUM_LEDS; i++) {
+      if (i <= slinkyPosition) {
+        leds[i] = currentColor;
+      } else {
+        leds[i] = CRGB::Black;
+      }
+    }
+    FastLED.show();
+
+    if (slinkyPosition == NUM_LEDS - 1) {
+      // log("Reverse ... towards pos 0");
+      delta = -1;
+      delay(holdTime);
+    } else if (slinkyPosition == 0) {
+      // log("Forward we go!");
+      delta = 1;
+      delay(holdTime);
+    }
+    slinkyPosition = (slinkyPosition + delta + NUM_LEDS) % NUM_LEDS;
+    delay(loopDelay);
+  }
+}
+
+void bounce() {
+  if (!repeat && doneTheNonRepeatingAnimationOnce == true) {
+    positionRed = 0;
+    positionWhite = 1;
+    positionBlue = 2;
+    delta = 1;
+    return;
+  }
+  doneTheNonRepeatingAnimationOnce = true;
+  // Set pixel color
+  for (int i=0;i<NUM_LEDS;i++) {
+    leds[positionRed] = currentColor;
+    leds[positionWhite] = currentColor;
+    leds[positionBlue] = currentColor;
+
+    // Show the pixels
+    FastLED.show();
+
+    // Set pixels back to Black for the next loop around.
+    leds[positionRed] = CRGB::Black;
+    leds[positionWhite] = CRGB::Black;
+    leds[positionBlue] = CRGB::Black;
+
+    // Set new position, moving (forward or backward) by delta.
+    // NUM_LEDS is added to the position before doing the modulo
+    // to cover cases where delta is a negative value.
+    if (positionWhite == NUM_LEDS - 1) {
+      // log("Reverse ... towards pos 0");
+      delta = -1;
+    } else if (positionBlue == 1) {
+      // log("Forward we go!");
+      delta = 1;
+    }
+    positionRed = (positionRed + delta + NUM_LEDS) % NUM_LEDS;
+    positionWhite = (positionWhite + delta + NUM_LEDS) % NUM_LEDS;
+    positionBlue = (positionBlue + delta + NUM_LEDS) % NUM_LEDS;
+    delay(loopDelay);
+  }
+}
+
+void bounce_backwards() {
+  if (!repeat && doneTheNonRepeatingAnimationOnce == true) {
+    positionRed = NUM_LEDS - 2;
+    positionWhite = NUM_LEDS - 1;
+    positionBlue = NUM_LEDS;
     delta = -1;
-    delay(holdTime);
-  } else if (edgesCenterPosition < -1) {
-    // log("Forward we go!");
-    edgesCenterPosition += 1;
-    delta = 1;
-    delay(holdTime);
-  }
-}
-
-
-void slinky_backwards() {
-  for (int i = 0; i < NUM_LEDS; i++) {
-    if (i > slinkyPosition) {
-      leds[i] = currentColor;
-    } else {
-      leds[i] = CRGB::Black;
-    }
-  }
-  FastLED.show();
-
-  if (slinkyPosition == NUM_LEDS - 1) {
-    // log("Reverse ... towards pos 0");
-    delta = -1;
-    delay(holdTime);
-  } else if (slinkyPosition == 0) {
-    // log("Forward we go!");
-    delta = 1;
-    delay(holdTime);
-  }
-  slinkyPosition = (slinkyPosition + delta + NUM_LEDS) % NUM_LEDS;
-}
-
-void slinky() {
-  for (int i = 0; i < NUM_LEDS; i++) {
-    if (i <= slinkyPosition) {
-      leds[i] = currentColor;
-    } else {
-      leds[i] = CRGB::Black;
-    }
-  }
-  FastLED.show();
-
-  if (slinkyPosition == NUM_LEDS - 1) {
-    // log("Reverse ... towards pos 0");
-    delta = -1;
-    delay(holdTime);
-  } else if (slinkyPosition == 0) {
-    // log("Forward we go!");
-    delta = 1;
-    delay(holdTime);
-  }
-  slinkyPosition = (slinkyPosition + delta + NUM_LEDS) % NUM_LEDS;
-}
-
-void bounce() {
+    return;
+  }
+  doneTheNonRepeatingAnimationOnce = true;
   // Set pixel color
-  leds[positionRed] = currentColor;
-  leds[positionWhite] = currentColor;
-  leds[positionBlue] = currentColor;
-
-  // Show the pixels
-  FastLED.show();
-
-  // Set pixels back to Black for the next loop around.
-  leds[positionRed] = CRGB::Black;
-  leds[positionWhite] = CRGB::Black;
-  leds[positionBlue] = CRGB::Black;
-
-  // Set new position, moving (forward or backward) by delta.
-  // NUM_LEDS is added to the position before doing the modulo
-  // to cover cases where delta is a negative value.
-  if (positionBlue == NUM_LEDS - 1) {
-    // log("Reverse ... towards pos 0");
-    delta = -1;
-  } else if (positionBlue == 1) {
-    // log("Forward we go!");
-    delta = 1;
-  }
-  positionRed = (positionRed + delta + NUM_LEDS) % NUM_LEDS;
-  positionWhite = (positionWhite + delta + NUM_LEDS) % NUM_LEDS;
-  positionBlue = (positionBlue + delta + NUM_LEDS) % NUM_LEDS;
+  for (int i=0;i<NUM_LEDS;i++) {
+    leds[positionRed] = currentColor;
+    leds[positionWhite] = currentColor;
+    leds[positionBlue] = currentColor;
+
+    // Show the pixels
+    FastLED.show();
+
+    // Set pixels back to Black for the next loop around.
+    leds[positionRed] = CRGB::Black;
+    leds[positionWhite] = CRGB::Black;
+    leds[positionBlue] = CRGB::Black;
+
+    // Set new position, moving (forward or backward) by delta.
+    // NUM_LEDS is added to the position before doing the modulo
+    // to cover cases where delta is a negative value.
+    if (positionWhite == NUM_LEDS - 1) {
+      // log("Reverse ... towards pos 0");
+      delta = -1;
+    } else if (positionBlue == 1) {
+      // log("Forward we go!");
+      delta = 1;
+    }
+    positionRed = (positionRed + delta + NUM_LEDS) % NUM_LEDS;
+    positionWhite = (positionWhite + delta + NUM_LEDS) % NUM_LEDS;
+    positionBlue = (positionBlue + delta + NUM_LEDS) % NUM_LEDS;
+    delay(loopDelay);
+  }
+}
+
+void circle() {
+  if (!repeat && doneTheNonRepeatingAnimationOnce == true) {
+    return;
+  }
+  doneTheNonRepeatingAnimationOnce = true;
+  // Set pixel color
+  delta = 1;
+  for (int i=0;i<NUM_LEDS;i++) {
+    leds[positionRed] = currentColor;
+    leds[positionWhite] = currentColor;
+    leds[positionBlue] = currentColor;
+
+    // Show the pixels
+    FastLED.show();
+
+    // Set pixels back to Black for the next loop around.
+    leds[positionRed] = CRGB::Black;
+    leds[positionWhite] = CRGB::Black;
+    leds[positionBlue] = CRGB::Black;
+
+    positionRed = (positionRed + delta + NUM_LEDS) % NUM_LEDS;
+    positionWhite = (positionWhite + delta + NUM_LEDS) % NUM_LEDS;
+    positionBlue = (positionBlue + delta + NUM_LEDS) % NUM_LEDS;
+    delay(loopDelay);
+  }
+}
+
+void circle_backwards() {
+  if (!repeat && doneTheNonRepeatingAnimationOnce == true) {
+    return;
+  }
+  doneTheNonRepeatingAnimationOnce = true;
+  // Set pixel color
+  delta = -1;
+  for (int i=0;i<NUM_LEDS;i++) {
+    leds[positionRed] = currentColor;
+    leds[positionWhite] = currentColor;
+    leds[positionBlue] = currentColor;
+
+    // Show the pixels
+    FastLED.show();
+
+    // Set pixels back to Black for the next loop around.
+    leds[positionRed] = CRGB::Black;
+    leds[positionWhite] = CRGB::Black;
+    leds[positionBlue] = CRGB::Black;
+
+    positionRed = (positionRed + delta + NUM_LEDS) % NUM_LEDS;
+    positionWhite = (positionWhite + delta + NUM_LEDS) % NUM_LEDS;
+    positionBlue = (positionBlue + delta + NUM_LEDS) % NUM_LEDS;
+    delay(loopDelay);
+  }
 }

 void off() {
   saveCurrentAnimation("solid_color");
   current_color = "black";
-  loopDelay = origLoopDelay;
-  holdTime = origHoldTime;
+  reset();
 }

 void reset() {
   loopDelay = origLoopDelay;
   holdTime = origHoldTime;
+  positionRed = 0;
+  positionWhite = 1;
+  positionBlue = 2;
+  delta = 1;
+  slinkyPosition = 1;
+  edgesCenterPosition = 0;
+
 }

 void handleMqttMessage(char *topic, byte *payload, unsigned int length) {
@@ -350,7 +503,6 @@
   if (results.size() >= 2) {
     // results[1] should be a function name
     char *theFunction = const_cast<char*>(results[1].c_str());
-    // log("theFunction: ", false);log(theFunction);
     if (strstr(valid_functions, theFunction)) {
       saveCurrentAnimation(theFunction);
     }
@@ -363,12 +515,23 @@
         loopDelay = tmpLoopDelay;
       }
     }
-    if (results.size() == 4) {
+    if (results.size() >= 4) {
       // if there's a fourth param, it will be the holdTime
       char *tmpHoldTimeChar = const_cast<char*>(results[3].c_str());
       uint16_t tmpHoldTime = atoi(tmpHoldTimeChar);
       if (tmpHoldTime > 0) {
         holdTime = tmpHoldTime;
+      }
+    }
+    if (results.size() >= 5) {
+      // if there's a fifth param, it will be the repeat boolean
+      char *tmpRepeat = const_cast<char*>(results[4].c_str());
+      if (strcmp(tmpRepeat, "false") == 0) {
+        repeat = false;
+        doneTheNonRepeatingAnimationOnce = false;
+      } else {
+        repeat = true;
+        doneTheNonRepeatingAnimationOnce = false;
       }
     }
   }
@@ -462,5 +625,4 @@
   mqttClient.loop();  // this is ESSENTIAL!
   set_color(current_color);
   currentAnimation();
-  delay(loopDelay);
-}
+}


Added code to calculate uncalibrated SPL readings from mic

function to determine SPL from ADC readings. May need a higher
resolution ADC, possibly with higher sample rate to allow FFT
calculations later on. (Maybe an ARM chip)

--- firmware/SensorBoard_node/SensorBoard_node.ino/SensorBoard_node.ino.ino
+++ firmware/SensorBoard_node/SensorBoard_node.ino/SensorBoard_node.ino.ino
@@ -44,7 +44,7 @@
 // define TSL2561 global variables
 long lux;
 float bar;
-int sound;
+float sound;
 float co;
 float no2;

@@ -173,7 +173,16 @@

   // ADMP401 mic for sound level
-  sound = analogRead(A0);  // read sound levels
+  float sumADC=0.0;
+  for(int i=0;i<5;i++)
+  {
+     sumADC = sumADC + analogRead(A0); // take avg of 5 readings
+  }
+  float averageADC = sumADC/5.0;
+  float volts = (averageADC/1023.0 * 3.3);
+  sound = (20 * log10(volts/0.007943)) - 42 + 94 - 60;  // VRMS = 0.007943; -42dB is sensitivity of ADMP401 mic; 1 Pa = 94 dB SPL RMS; 60dB is gain of amplifier
+  // the above is uncalibrated sound level - needs to be calibrated with reference to an accurate sound level meter in varying SPLs, frequencies and environments.
+

   // MPL3115A2 air pressure sensor
@@ -236,7 +245,7 @@
   dtostrf(bar, 3, 2, _g);
   dtostrf(lux, 1, 0, _l);
   dtostrf(no2, 1, 0, _n);
-  dtostrf(sound, 1, 0, _s);
+  dtostrf(sound, 1, 1, _s);
   dtostrf(tvoc, 1, 0, _v);
   delay(50);


Create MultipleButtons.ino

Create MultipleButtons.ino


Revert "Fixes delay of serial input when using parseInt (#7)"

This reverts commit 440675814c7acddded0066cdc88f473d5ad12e3f.

--- arduino/serial_protocol/serial_protocol.ino
+++ arduino/serial_protocol/serial_protocol.ino
@@ -5,34 +5,64 @@
 const int pin_right   = 6;

 // Initializing bit masks
-const int forward_mask = 1 << 3; // 1000
-const int reverse_mask = 1 << 2; // 0100
-const int left_mask    = 1 << 1; // 0010
-const int right_mask   = 1 << 0; // 0001
+const int forward_mask = 8; // 1000
+const int reverse_mask = 4; // 0100
+const int left_mask    = 2; // 0010
+const int right_mask   = 1; // 0001

-int protocol_num = -1;
+int protocol_num; // number of current state
+int forward_value = 0;
+int reverse_value = 0;
+int left_value = 0;
+int right_value = 0; // values that will be read from Serial after applying mask
+boolean commandComplete = false;

 void setup() {
-    pinMode(pin_forward, OUTPUT);
-    pinMode(pin_reverse, OUTPUT);
-    pinMode(pin_left,    OUTPUT);
-    pinMode(pin_right,   OUTPUT);
-
-    digitalWrite(pin_forward, HIGH);
-    digitalWrite(pin_reverse, HIGH);
-    digitalWrite(pin_left,    HIGH);
-    digitalWrite(pin_right,   HIGH);
-
-    Serial.begin(9600);
+  pinMode(pin_forward, OUTPUT);
+  pinMode(pin_reverse, OUTPUT);
+  pinMode(pin_left,    OUTPUT);
+  pinMode(pin_right,   OUTPUT);
+
+  digitalWrite(pin_forward, HIGH);
+  digitalWrite(pin_reverse, HIGH);
+  digitalWrite(pin_left,    HIGH);
+  digitalWrite(pin_right,   HIGH);
+
+  Serial.begin(9600);
 }

 void loop(){
-    if((protocol_num = Serial.read()) == -1) return;
+  if(commandComplete){
+    Serial.println();
+    Serial.println("Current move:");
+    Serial.println(protocol_num);
+
+    forward_value = protocol_num & forward_mask;
+    reverse_value = protocol_num & reverse_mask;
+    left_value    = protocol_num & left_mask;
+    right_value   = protocol_num & right_mask;
+
+    Serial.print(forward_value);
+    Serial.print(reverse_value);
+    Serial.print(left_value);
+    Serial.print(right_value);

-    // sets HIGH for no motion, and LOW to activate motion as we use ground pins to shorten the circuit
-    digitalWrite(pin_forward, (protocol_num & forward_mask) ? LOW : HIGH);
-    digitalWrite(pin_reverse, (protocol_num & reverse_mask) ? LOW : HIGH);
-    digitalWrite(pin_left,    (protocol_num & left_mask) ? LOW : HIGH);
-    digitalWrite(pin_right,   (protocol_num & right_mask) ? LOW : HIGH);
+    digitalWrite(pin_forward, forward_value?LOW:HIGH);
+    digitalWrite(pin_reverse, reverse_value?LOW:HIGH);
+    digitalWrite(pin_left,    left_value?LOW:HIGH);
+    digitalWrite(pin_right,   right_value?LOW:HIGH);
+
+    commandComplete = false;
+  }
 }

+void serialEvent() {
+  if(Serial.available() > 0){
+    protocol_num = Serial.parseInt();
+  }
+  while(Serial.available() > 0){
+    Serial.read(); // clean up whatever left in Serial
+  }
+  commandComplete = true;
+}
+


removed the delay at zero for player scoring

--- range/arduino/control_module/control_module.ino
+++ range/arduino/control_module/control_module.ino
@@ -159,14 +159,14 @@
   SCOREBD.println(F("D2"));
   delay(1000);
   SCOREBD.println(F("D1"));
-  delay(1000);
-  SCOREBD.println(F("Z"));
-  delay(100);
+  delay(900);
   //start listening to the targets
   TARGET.println(F("S"));
   TARGET.println(F("D"));
   //start the timer
   TIMER.println(F("S"));
+  //then display the zero
+  SCOREBD.println(F("Z"));
   start_time = millis();
   last_update = millis() + RESEND_TIMER;
 }


Update GPRS_SendSMS.ino

Update GPRS_SendSMS.ino

--- examples/GPRS_SendSMS/GPRS_SendSMS.ino
+++ examples/GPRS_SendSMS/GPRS_SendSMS.ino
@@ -10,9 +10,9 @@
 // текст сообщения, которое будем отправлять
 #define MESSAGE  "Hello from GPRS Shield"

-// создаём объект класса GPRS и передаём ему скорость 9600 бод
+// создаём объект класса GPRS. По умолчанию скорость общения с ним 9600 бод
 // с помощью него будем давать команды GPRS шилду
-GPRS gprs(9600);
+GPRS gprs();

 void setup()
 {


Mqtt Example improvements (#444)

Mqtt Example improvements (#444)

* Provide more info to user in IRMQTTServer
* Report the following on the main web page:
  - time since boot
  - last mqtt command request
  - time since last mqtt command request
  - Total nr. of IR message send requests
  - mqtt clientid
  - Version of IRremoteESP8266 it was compiled with.
* Other minor HTML changes.
* [BUG]
  - Make mqtt clientid unique. (wasn't working as intended)
  - Add missing Haier AC case statement.
* Avoid potential HTML/XSS problems when displaying the
  mqtt command received. (Untrusted input)

--- examples/IRMQTTServer/IRMQTTServer.ino
+++ examples/IRMQTTServer/IRMQTTServer.ino
@@ -167,14 +167,18 @@
 uint32_t lastReconnectAttempt = 0;  // MQTT last attempt reconnection number
 bool boot = true;
 bool ir_lock = false;  // Primitive locking for gating the IR LED.
+uint32_t sendReqCounter = 0;

 #ifdef MQTT_ENABLE
+String lastMqttCmd = "None";
+uint32_t lastMqttCmdTime = 0;
+
+
 // MQTT client parameters
 void callback(char* topic, byte* payload, unsigned int length);
 PubSubClient mqtt_client(MQTT_SERVER, MQTT_PORT, callback, espClient);
 // Create a unique MQTT client id.
-const char* mqtt_clientid = String(MQTTprefix +
-                                   String(ESP.getChipId(), HEX)).c_str();
+String mqtt_clientid = MQTTprefix + String(ESP.getChipId(), HEX);
 #endif  // MQTT_ENABLE

 // Debug messages get sent to the serial port.
@@ -183,6 +187,50 @@
   uint32_t now = millis();
   Serial.printf("%07u.%03u: %s\n", now / 1000, now % 1000, str.c_str());
 #endif  // DEBUG
+}
+
+String timeSince(uint32_t const start) {
+  if (start == 0)
+    return "Never";
+  uint32_t diff = 0;
+  uint32_t now = millis();
+  if (start < now)
+    diff = now - start;
+  else
+    diff = UINT32_MAX - start + now;
+  diff /= 1000;  // Convert to seconds.
+  if (diff == 0)  return "Now";
+
+  // Note: millis() can only count up to 45 days, so uint8_t is safe.
+  uint8_t days = diff / (60 * 60 * 24);
+  uint8_t hours = (diff / (60 * 60)) % 24;
+  uint8_t minutes = (diff / 60) % 60;
+  uint8_t seconds = diff % 60;
+
+  String result = "";
+  if (days)
+    result += String(days) + " day";
+  if (days > 1)  result += "s";
+  if (hours)
+    result += " " + String(hours) + " hour";
+  if (hours > 1)  result += "s";
+  if (minutes)
+    result += " " + String(minutes) + " minute";
+  if (minutes > 1)  result += "s";
+  if (seconds)
+    result += " " + String(seconds) + " second";
+  if (seconds > 1)  result += "s";
+  result.trim();
+  return result + " ago";
+}
+
+// Quick and dirty check for any unsafe chars in a string
+// that may cause HTML shenanigans. e.g. An XSS.
+bool hasUnsafeHTMLChars(String input) {
+  static char unsafe[] = "';!-\"<>=&{}()";
+  for (uint8_t i = 0; unsafe[i]; i++)
+    if (input.indexOf(unsafe[i]) != -1) return true;
+  return false;
 }

 // Root web page with example usage etc.
@@ -192,13 +240,24 @@
     ""
     "
ESP8266 IR MQTT Server
"
     "

"
-    "
Connection details
"
-    "
IP address: " + WiFi.localIP().toString() + "
"
+    "
Information
"
+    "
IP address: " + WiFi.localIP().toString() + "
"
+    "Booted: " + timeSince(1) + "
" +
+    "IR Lib Version: " _IRREMOTEESP8266_VERSION_ "
"
+    "Total send requests: " + String(sendReqCounter) + "
"
 #ifdef MQTT_ENABLE
-    "
MQTT server: " MQTT_SERVER ":" + String(MQTT_PORT) + " ("+
-    (mqtt_client.connected() ? "Connected" : "Disconnected") + ")
"
+    "
MQTT Information
"
+    "
Server: " MQTT_SERVER ":" + String(MQTT_PORT) + " (" +
+    (mqtt_client.connected() ? "Connected" : "Disconnected") + ")
"
+    "Client id: " + mqtt_clientid + "
"
     "Command topic: " MQTTcommand "
"
-    "Acknowledgements topic: " MQTTack "
"
+    "Acknowledgements topic: " MQTTack "
"
+    "Last command seen: " +
+    // lastMqttCmd is unescaped untrusted input.
+    // Avoid any possible HTML/XSS when displaying it.
+    (hasUnsafeHTMLChars(lastMqttCmd) ?
+        "Contains unsafe HTML characters" : lastMqttCmd) +
+    " (" + timeSince(lastMqttCmdTime) + ")
"
 #endif  // MQTT_ENABLE
     "

"
     "
Hardcoded examples
"
@@ -823,9 +882,10 @@
     debug("Attempting MQTT connection to " MQTT_SERVER ":" + String(MQTT_PORT) +
           "... ");
     if (mqtt_user && mqtt_password)
-      connected = mqtt_client.connect(mqtt_clientid, mqtt_user, mqtt_password);
+      connected = mqtt_client.connect(mqtt_clientid.c_str(), mqtt_user,
+                                      mqtt_password);
     else
-      connected = mqtt_client.connect(mqtt_clientid);
+      connected = mqtt_client.connect(mqtt_clientid.c_str());
     if (connected) {
     // Once connected, publish an announcement...
       mqtt_client.publish(MQTTack, "Connected");
@@ -1021,6 +1081,7 @@
     case TROTEC:  // 28
     case TOSHIBA_AC:  // 32
     case FUJITSU_AC:  // 33
+    case HAIER_AC:  // 38
       parseStringAndSendAirCon(ir_type, code_str);
       break;
 #if SEND_DENON
@@ -1118,7 +1179,7 @@
       break;
 #endif
   }
-
+  sendReqCounter++;
   // Release the lock.
   ir_lock = false;

@@ -1170,6 +1231,9 @@
   // Make a copy of the callback string as strtok destroys it.
   char* callback_c_str = strdup(callback_str.c_str());
   debug("MQTT Payload (raw): " + callback_str);
+  // Save the message as the last command seen (global).
+  lastMqttCmd = callback_str;
+  lastMqttCmdTime = millis();

   // Get the numeric protocol type.
   int ir_type = strtoul(strtok_r(callback_c_str, ",", &tok_ptr), NULL, 10);


with avoid can modify easily

with avoid can modify easily


Delete remaining example

Delete remaining example

--- examples/graphicstest/graphicstest.ino
+++ examples/graphicstest/graphicstest.ino
@@ -1,332 +0,0 @@
-/***************************************************
-  This is our GFX example for the Adafruit ILI9341 Breakout and Shield
-  ----> http://www.adafruit.com/products/1651
-
-  Check out the links above for our tutorials and wiring diagrams
-  These displays use SPI to communicate, 4 or 5 pins are required to
-  interface (RST is optional)
-  Adafruit invests time and resources providing this open source code,
-  please support Adafruit and open-source hardware by purchasing
-  products from Adafruit!
-
-  Written by Limor Fried/Ladyada for Adafruit Industries.
-  MIT license, all text above must be included in any redistribution
- ****************************************************/
-
-
-#include "SPI.h"
-#include <ssd1351.h>
-
-// use this to do Color c = RGB(...) instead of `RGB c = RGB(...)` or ssd1351::LowColor c = RGB(...)
-// because it's slightly faster and guarantees you won't be sending wrong colours to the display.
-typedef ssd1351::HighColor Color;
-
-auto display = ssd1351::SSD1351<Color, ssd1351::SingleBuffer, 128, 96>();
-
-void setup() {
-  tft.begin();
-  tft.fillScreen(ssd1351::RGB());
-  tft.println("Waiting for Arduino Serial Monitor...");
-
-  Serial.begin(9600);
-  while (!Serial) ; // wait for Arduino Serial Monitor
-  Serial.println("SSD1351 Test!");
-
-  Serial.println(F("Benchmark                Time (microseconds)"));
-
-  Serial.print(F("Screen fill              "));
-  Serial.println(testFillScreen());
-  delay(200);
-
-  Serial.print(F("Lines                    "));
-  Serial.println(testLines(ILI9341_CYAN));
-  delay(200);
-
-  Serial.print(F("Horiz/Vert Lines         "));
-  Serial.println(testFastLines(ILI9341_RED, ILI9341_BLUE));
-  delay(200);
-
-  Serial.print(F("Rectangles (outline)     "));
-  Serial.println(testRects(ILI9341_GREEN));
-  delay(200);
-
-  Serial.print(F("Rectangles (filled)      "));
-  Serial.println(testFilledRects(ILI9341_YELLOW, ILI9341_MAGENTA));
-  delay(200);
-
-  Serial.print(F("Circles (filled)         "));
-  Serial.println(testFilledCircles(10, ILI9341_MAGENTA));
-
-  Serial.print(F("Circles (outline)        "));
-  Serial.println(testCircles(10, ILI9341_WHITE));
-  delay(200);
-
-  Serial.print(F("Triangles (outline)      "));
-  Serial.println(testTriangles());
-  delay(200);
-
-  Serial.print(F("Triangles (filled)       "));
-  Serial.println(testFilledTriangles());
-  delay(200);
-
-  Serial.print(F("Rounded rects (outline)  "));
-  Serial.println(testRoundRects());
-  delay(200);
-
-  Serial.print(F("Rounded rects (filled)   "));
-  Serial.println(testFilledRoundRects());
-  delay(200);
-
-  Serial.println(F("Done!"));
-
-}
-
-
-void loop(void) {
-  for(uint8_t rotation=0; rotation<4; rotation++) {
-    tft.setRotation(rotation);
-    testText();
-    delay(1000);
-  }
-}
-
-unsigned long testFillScreen() {
-  unsigned long start = micros();
-  tft.fillScreen(ILI9341_BLACK);
-  tft.fillScreen(ILI9341_RED);
-  tft.fillScreen(ILI9341_GREEN);
-  tft.fillScreen(ILI9341_BLUE);
-  tft.fillScreen(ILI9341_BLACK);
-  return micros() - start;
-}
-
-unsigned long testText() {
-  tft.fillScreen(ILI9341_BLACK);
-  unsigned long start = micros();
-  tft.setCursor(0, 0);
-  tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(1);
-  tft.println("Hello World!");
-  tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(2);
-  tft.println(1234.56);
-  tft.setTextColor(ILI9341_RED);    tft.setTextSize(3);
-  tft.println(0xDEADBEEF, HEX);
-  tft.println();
-  tft.setTextColor(ILI9341_GREEN);
-  tft.setTextSize(5);
-  tft.println("Groop");
-  tft.setTextSize(2);
-  tft.println("I implore thee,");
-  tft.setTextSize(1);
-  tft.println("my foonting turlingdromes.");
-  tft.println("And hooptiously drangle me");
-  tft.println("with crinkly bindlewurdles,");
-  tft.println("Or I will rend thee");
-  tft.println("in the gobberwarts");
-  tft.println("with my blurglecruncheon,");
-  tft.println("see if I don't!");
-  return micros() - start;
-}
-
-unsigned long testLines(uint16_t color) {
-  unsigned long start, t;
-  int           x1, y1, x2, y2,
-                w = tft.width(),
-                h = tft.height();
-
-  tft.fillScreen(ILI9341_BLACK);
-
-  x1 = y1 = 0;
-  y2    = h - 1;
-  start = micros();
-  for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);
-  x2    = w - 1;
-  for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);
-  t     = micros() - start; // fillScreen doesn't count against timing
-
-  tft.fillScreen(ILI9341_BLACK);
-
-  x1    = w - 1;
-  y1    = 0;
-  y2    = h - 1;
-  start = micros();
-  for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);
-  x2    = 0;
-  for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);
-  t    += micros() - start;
-
-  tft.fillScreen(ILI9341_BLACK);
-
-  x1    = 0;
-  y1    = h - 1;
-  y2    = 0;
-  start = micros();
-  for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);
-  x2    = w - 1;
-  for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);
-  t    += micros() - start;
-
-  tft.fillScreen(ILI9341_BLACK);
-
-  x1    = w - 1;
-  y1    = h - 1;
-  y2    = 0;
-  start = micros();
-  for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);
-  x2    = 0;
-  for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);
-
-  return micros() - start;
-}
-
-unsigned long testFastLines(uint16_t color1, uint16_t color2) {
-  unsigned long start;
-  int           x, y, w = tft.width(), h = tft.height();
-
-  tft.fillScreen(ILI9341_BLACK);
-  start = micros();
-  for(y=0; y<h; y+=5) tft.drawFastHLine(0, y, w, color1);
-  for(x=0; x<w; x+=5) tft.drawFastVLine(x, 0, h, color2);
-
-  return micros() - start;
-}
-
-unsigned long testRects(uint16_t color) {
-  unsigned long start;
-  int           n, i, i2,
-                cx = tft.width()  / 2,
-                cy = tft.height() / 2;
-
-  tft.fillScreen(ILI9341_BLACK);
-  n     = min(tft.width(), tft.height());
-  start = micros();
-  for(i=2; i<n; i+=6) {
-    i2 = i / 2;
-    tft.drawRect(cx-i2, cy-i2, i, i, color);
-  }
-
-  return micros() - start;
-}
-
-unsigned long testFilledRects(uint16_t color1, uint16_t color2) {
-  unsigned long start, t = 0;
-  int           n, i, i2,
-                cx = tft.width()  / 2 - 1,
-                cy = tft.height() / 2 - 1;
-
-  tft.fillScreen(ILI9341_BLACK);
-  n = min(tft.width(), tft.height());
-  for(i=n; i>0; i-=6) {
-    i2    = i / 2;
-    start = micros();
-    tft.fillRect(cx-i2, cy-i2, i, i, color1);
-    t    += micros() - start;
-    // Outlines are not included in timing results
-    tft.drawRect(cx-i2, cy-i2, i, i, color2);
-  }
-
-  return t;
-}
-
-unsigned long testFilledCircles(uint8_t radius, uint16_t color) {
-  unsigned long start;
-  int x, y, w = tft.width(), h = tft.height(), r2 = radius * 2;
-
-  tft.fillScreen(ILI9341_BLACK);
-  start = micros();
-  for(x=radius; x<w; x+=r2) {
-    for(y=radius; y<h; y+=r2) {
-      tft.fillCircle(x, y, radius, color);
-    }
-  }
-
-  return micros() - start;
-}
-
-unsigned long testCircles(uint8_t radius, uint16_t color) {
-  unsigned long start;
-  int           x, y, r2 = radius * 2,
-                w = tft.width()  + radius,
-                h = tft.height() + radius;
-
-  // Screen is not cleared for this one -- this is
-  // intentional and does not affect the reported time.
-  start = micros();
-  for(x=0; x<w; x+=r2) {
-    for(y=0; y<h; y+=r2) {
-      tft.drawCircle(x, y, radius, color);
-    }
-  }
-
-  return micros() - start;
-}
-
-unsigned long testTriangles() {
-  unsigned long start;
-  int           n, i, cx = tft.width()  / 2 - 1,
-                      cy = tft.height() / 2 - 1;
-
-  tft.fillScreen(ILI9341_BLACK);
-  n     = min(cx, cy);
-  start = micros();
-  for(i=0; i<n; i+=5) {
-    tft.drawTriangle(
-      cx    , cy - i, // peak
-      cx - i, cy + i, // bottom left
-      cx + i, cy + i, // bottom right
-      tft.color565(0, 0, i));
-  }
-
-  return micros() - start;
-}
-
-unsigned long testFilledTriangles() {
-  unsigned long start, t = 0;
-  int           i, cx = tft.width()  / 2 - 1,
-                   cy = tft.height() / 2 - 1;
-
-  tft.fillScreen(ILI9341_BLACK);
-  start = micros();
-  for(i=min(cx,cy); i>10; i-=5) {
-    start = micros();
-    tft.fillTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,
-      tft.color565(0, i, i));
-    t += micros() - start;
-    tft.drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,
-      tft.color565(i, i, 0));
-  }
-
-  return t;
-}
-
-unsigned long testRoundRects() {
-  unsigned long start;
-  int           w, i, i2,
-                cx = tft.width()  / 2 - 1,
-                cy = tft.height() / 2 - 1;
-
-  tft.fillScreen(ILI9341_BLACK);
-  w     = min(tft.width(), tft.height());
-  start = micros();
-  for(i=0; i<w; i+=6) {
-    i2 = i / 2;
-    tft.drawRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(i, 0, 0));
-  }
-
-  return micros() - start;
-}
-
-unsigned long testFilledRoundRects() {
-  unsigned long start;
-  int           i, i2,
-                cx = tft.width()  / 2 - 1,
-                cy = tft.height() / 2 - 1;
-
-  tft.fillScreen(ILI9341_BLACK);
-  start = micros();
-  for(i=min(tft.width(), tft.height()); i>20; i-=6) {
-    i2 = i / 2;
-    tft.fillRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(0, i, 0));
-  }
-
-  return micros() - start;
-}


per-servo config

per-servo config

--- RobotAnimations/RobotAnimations.ino
+++ RobotAnimations/RobotAnimations.ino
@@ -25,9 +25,28 @@

 auto _servoMux = Adafruit_PWMServoDriver();

-const int kServoMin = 150;
-const int kServoMax = 500;
-const vector<short> kServoCenter = { 0, 320, 320, 345, 290, 330, 320, 345, 290 };
+struct ServoSpec
+{
+  int pulseMin;
+  int pulseMax;
+};
+const ServoSpec kServoA0090 = { 150, 650 };
+const ServoSpec kServoKy66 = { 150, 520 };
+
+struct ServoConfig
+{
+  ServoSpec spec;
+  int center;
+  bool flip;
+};
+
+// 150 (0) -> 400 (90) -> 650 (180)
+//const vector<short> kServoCenter = { 0, 320, 320, 345, 290, 330, 320, 345, 290 };
+const vector<ServoConfig> kServoSpecs = {
+  ServoConfig(),
+  { kServoKy66, 0, false}, { kServoKy66, 90, true}, { kServoKy66, 90, false}, { kServoA0090, 0, false},
+  { kServoKy66, 0, false}, { kServoKy66, 90, true}, { kServoKy66, 90, false}, { kServoA0090, 0, false}
+};
 LatchButton leftButton(4, InputPinMode::PullDown);
 LatchButton rightButton(5, InputPinMode::PullDown);

@@ -37,7 +56,7 @@
 int _frameIndex = 0;
 int _frameCount = 1;
 int _lastFrameMillis = 0;
-
+
 void setup()
 {
   Serial.begin(115200);
@@ -68,7 +87,7 @@
   _frameIndex = 0;
   _frameCount = kActions[_actionIndex].frames.size();
   _lastFrameMillis = millis();
-
+
   serialPrintfln("starting animation %s (%d frames)", kActions[_actionIndex].name, kActions[_actionIndex].frames.size());
 }

@@ -102,11 +121,15 @@
     advanceFrame(_actionIndex == _idleAction);
   }

-  const auto& frame = kActions[_actionIndex].frames[_frameIndex];
+  auto& frame = kActions[_actionIndex].frames[_frameIndex];

-  for (int servoIndex = 0; servoIndex < kServoCenter.size(); servoIndex++)
+  for (int servoIndex = 0; servoIndex < kServoSpecs.size(); servoIndex++)
   {
-    int pulseLen = (float(frame.boneAngles[servoIndex]) / 360) * (kServoMax - kServoMin) + kServoMin;
+    auto& servoConfig = kServoSpecs[servoIndex];
+    auto& spec = servoConfig.spec;
+    float angle = frame.boneAngles[servoIndex] + servoConfig.center;
+    angle = servoConfig.flip ? (180 - angle) : angle;
+    int pulseLen = (angle / 180) * (spec.pulseMax - spec.pulseMin) + spec.pulseMin;
     _servoMux.setPWM(servoIndex, 0, pulseLen);
   }