Use SPI interrupts to drive the screen on after row change

--- avr/avr.ino
+++ avr/avr.ino
@@ -56,6 +56,7 @@
 volatile uint8_t may_flip = 0;
 volatile uint16_t pwm_lengths[] = { 0x30, 0x10, 0x06 }; // PWM cycle lengths
 uint8_t pwm_i = 0; // Current PWM cycle
+volatile uint8_t spi_row_change = false;

 // Hardware configuration
 #define PIN_COL_BIT0 A2
@@ -94,9 +95,11 @@
 	pinMode(MOSI, OUTPUT);
 	pinMode(SCK, OUTPUT);

-	// Initialize serial and SPI
+	// Initialize serial via USB
 	Serial.begin(19200);
-	SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0); // SPI master, clock rate fck/16
+
+	// Configure SPI as master, clock rate fck/16, enable interrupts
+	SPCR = _BV(SPIE) | _BV(SPE) | _BV(MSTR) | _BV(SPR0);

 	// Timer magic
 	cli();
@@ -220,16 +223,11 @@
 	// Are we moving to next segment?
 	if (pwm_i == 0) {
 		if (col_i == 0) {
-			// Busy-write
+			// Switch the row on row driver first and
+			// postpone turning on the screen.
+			spi_row_change = true;
 			SPDR = 1 << row_i;
-			while(!(SPSR & (1<<SPIF)));
-
-			// Latch the written row info
-			digitalWrite(PIN_ROW_LATCH, LOW);
-			digitalWrite(PIN_ROW_LATCH, HIGH);
-
 			pick_column();
-			screen_on();
 		} else {
 			// Switch column and turn on the screen.
 			pick_column();
@@ -298,3 +296,16 @@
 	// because it completes before the next timer interrupt.
 	SPDR = buf_front[pwm_i][8*row_i+col_i];
 }
+
+// SPI interrupt is only used when changing the row. Writes to LED
+// register are guaranteed (I hope) to complete in time.
+ISR(SPI_STC_vect)
+{
+	if (spi_row_change) {
+		// Latch the written row info
+		digitalWrite(PIN_ROW_LATCH, LOW);
+		digitalWrite(PIN_ROW_LATCH, HIGH);
+		spi_row_change = false;
+		screen_on();
+	}
+}


Minor adjustments

Minor adjustments

--- payload/payload.ino
+++ payload/payload.ino
@@ -1,5 +1,5 @@
 #include <Wire.h>
-#include "Adafruit_BMP085.h"
+#include <Adafruit_BMP085.h>

 /*
  * BMP180 setup instructions:
@@ -11,56 +11,61 @@
  * ----------------------------------------
 */

-Adafruit_BMP085 barometer;
+Adafruit_BMP085 bmp;

 void setup() {
-  Serial.begin(9600); // 9600 bits/second
-  if (!barometer.begin()) {
-  	Serial.println("No BMP085 sensor found.");
+  Serial.begin(2000000); // bits/second
+  if (!bmp.begin()) {
+  	Serial.println("No BMP085 sensor found. Program will not proceed.");
   	while (1) { /* Trap the thread. */ }
   }
 }

 void printBaroData() {

-    // Get temperature.
-    Serial.print("Temperature = ");
-    Serial.print(barometer.readTemperature());
-    Serial.println(" *C");
+  // Get temperature.
+  Serial.print("Temperature = ");
+  Serial.print(bmp.readTemperature());
+  Serial.println(" *C");

-    // Get pressure at sensor.
-    Serial.print("Pressure = ");
-    Serial.print(barometer.readPressure());
-    Serial.println(" Pa");
+  // Get pressure at sensor.
+  Serial.print("Pressure = ");
+  Serial.print(bmp.readPressure());
+  Serial.println(" Pa");

-    // Calculate pressure at 0 MSL. (0 meters mean sea-level)
-    Serial.print("Calculated pressure at 0 MSL = ");
-    Serial.print(barometer.readSealevelPressure());
-    Serial.println(" Pa");
+  // Calculate pressure at 0 MSL. (0 meters mean sea-level)
+  Serial.print("Calculated pressure at 0 MSL = ");
+  Serial.print(bmp.readSealevelPressure());
+  Serial.println(" Pa");

-    // Get pressure altitude:
-    // altitude with (default) altimeter setting at 101325 Pascals == 1013.25 millibars
-    // == 29.92 inches mercury (i.e., std. pressure)
-    Serial.print("Pressure altitude = ");
-    Serial.print(barometer.readAltitude());
-    Serial.println(" meters");
+  // Get pressure altitude:
+  // altitude with (default) altimeter setting at 101325 Pascals == 1013.25 millibars
+  // == 29.92 inches mercury (i.e., std. pressure)
+  Serial.print("Pressure altitude = ");
+  Serial.print(bmp.readAltitude());
+  Serial.println(" meters");

-    // TODO:
-    // Density altitude:
-    // pressure altitude corrected for nonstandard temperature
-    // High density altitude (High, Hot, and Humid) means decreased performance.
+  // TODO:
+  // Density altitude:
+  // pressure altitude corrected for nonstandard temperature
+  // High density altitude (High, Hot, and Humid) means decreased performance.

-    // Get indicated altitude:
-    // pressure altitude corrected for non-standard pressure, with altimeter
-    // setting 1015 millibars == 101500 Pascals
-    // For pressure conversions, visit NOAA at: https://www.weather.gov/media/epz/wxcalc/pressureConversion.pdf.
-    Serial.print("Indicated altitude = ");
-    Serial.print(barometer.readAltitude(101500));
-    Serial.println(" meters");
+  // Get indicated altitude:
+  // pressure altitude corrected for non-standard pressure, with
+  // altimeter setting (in Pascals) as argument
+  // (29.92 inches mercury == 101325 Pascals == 1013.25 millibars)
+  // For pressure conversions, visit NOAA at: https://www.weather.gov/media/epz/wxcalc/pressureConversion.pdf.
+  Serial.print("Indicated altitude =                                  ");
+  Serial.print(bmp.readAltitude());
+  Serial.println(" meters");
+  Serial.print("Indicated altitude with sensor pressure adjustment =  ");
+  Serial.print(bmp.readAltitude((float)bmp.readPressure()));
+  Serial.println(" meters");

-    Serial.println();
+  Serial.println();
 }
+
 void loop() {
-    printBaroData();
-    delay(350);
+  printBaroData();
+  delay(200);
 }


Added IRsend capability. Moved many strings into PROGMEM.

--- analogjson.ino
+++ analogjson.ino
@@ -1,29 +1,31 @@
-//
+//
 // Copyright (c) 2015, 2016 Neil Webber.
 //
 // See LICENSE file for licensing terms.
 //

-// oddly, without this at the top of the file, function definitions split
-// across lines after the type don't work. Some arduino environment bug.
-// The FIRST function encountered must have the type and the function name
-// on ONE line. Others after can be split. Very odd ... didn't chase down.
-// August 2016 toolchain 1.6.11 on Mac
-void FIRST_FUNCTION_BUG() {}
-
-
-#include <SPI.h>
+// A program for reading/writing digital IO pins with a JSON HTTP
+// interface, PLUS the ability to send IR codes. It's really
+// two different applications that are similar, rolled into one...
+
+#include <avr/pgmspace.h>
 #include <Ethernet.h>
-
 #include "jsmn.h"     // JSON parser

-// A NOTE ABOUT STRINGS
-//
-// There are many string literals in here. They could (should?) be moved
-// into program memory and accessed via PROGMEM hacks. But that makes
-// the code ugly. As this is mostly for example purposes, I've chosen to
-// leave them be. Everything fits within 2K still at the moment.
-//
+// If you only need the digital IO (no IR), undefine IR_IO to save footprint.
+// Or, vice-versa if you only need the IR code.
+#define DIGITAL_IO 1        // comment out to delete raw digital I/O
+#define IR_SEND 1           // comment out to delete IR sendings
+
+#if defined(IR_SEND) || defined(IR_RECV)
+#include "IRremote.h"
+#ifdef IR_SEND
+IRsend irsend;
+#endif
+#endif
+
+int busy_pin = -1;
+

 // ---------------------
 // Arbitrary Size Limits
@@ -37,7 +39,7 @@
 //      -- the biggest URL you are going to use
 //      -- the biggest JSON data you are going to exchange. This is
 //         usually the bigger requirement; if you want to send multiple
-//         pins in a single request the syntax adds up fast.
+//         items in a single POST the syntax adds up fast.
 //
 // Similarly, you need to set the MAXJSMNTOKENS to be large enough so that
 // the JSMN parser can parse whatever JSON you send.
@@ -45,15 +47,15 @@
 // In both cases if something comes in that is too large it will just be
 // discarded and an error returned to the client.
 //
-// These default constants allow most requests to have a few pins specified.
+// These default constants allow most small/simple requests.
 //

 #ifndef REQUESTBUFFERSIZE
-#define REQUESTBUFFERSIZE 200
+#define REQUESTBUFFERSIZE 450
 #endif

 #ifndef MAXJSMNTOKENS
-#define MAXJSMNTOKENS 30
+#define MAXJSMNTOKENS 50
 #endif

 // The MAC address this Arduino will use
@@ -71,7 +73,8 @@
 EthernetServer server(80);

 // version string
-const char revision[] = "20150826.1";
+// since this is always served in JSON dictionary form, that's included here
+const char revision_json[] PROGMEM = "\"serverVersion\": \"20161104.1\"";

 // this counter is for statistics; reported via status API
 unsigned long requests_processed = 0;
@@ -82,8 +85,7 @@
 // it cannot return the correct answer for time spans greater than
 // the total wrap-around time of approximately 50 days.

-unsigned long
-elapsed_time(unsigned long t0)
+unsigned long elapsed_time(unsigned long t0)
 {
     unsigned long t1 = millis();
     if (t1 >= t0)
@@ -113,7 +115,6 @@
     }
 }

-
 // ---------------
 //  JSMN helpers
 // ---------------
@@ -127,10 +128,47 @@
 //   tp is a jsmn token pointing into (start/stop indices into) buf.
 //   Compare those characters against "k" with strcmp semantics.
 //
-int
-jsmn_strcmp(const jsmntok_t *tp, const char *k, const char *buf)
+int jsmn_strcmp(const jsmntok_t *tp, const char *k, const char *buf)
 {
     return strncmp(k, &buf[tp->start], tp->end - tp->start);
+}
+
+
+// Integer conversion: unsigned long
+//   tp is a jsmn token pointing into (start/stop indices into) buf
+//   Convert it to an unsigned long, stopping at the first non-digit character.
+//
+// Returns 0 if nothing converted, which of course is indistinguishable
+// from an actual 0 converted (if you care about the distinction you need
+// to do more validation before calling this)
+
+unsigned long jsmn_atoul(const jsmntok_t *tp, const char *buf)
+{
+    unsigned long rv = 0;
+    int base = 10;
+    int startpos = tp->start;
+
+    // see if hex. NOTE: Hex is illegal JSON, you really shouldn't send it.
+    if ((tp->end - startpos > 2) &&
+        buf[startpos] == '0' &&
+        (buf[startpos+1] == 'x' || buf[startpos+1] == 'X')) {
+        base = 16;
+        startpos += 2;
+    }
+
+    for (int i = startpos; i < tp->end; i++) {
+        char c = buf[i];
+
+        if (isdigit(c)) {
+            rv *= base;
+            rv += (c - '0');
+        } else if (isHexadecimalDigit(c)) {
+            rv *= base;
+            rv += (10 + tolower(c) - 'a');
+        } else
+            break;
+    }
+    return rv;
 }

 // Integer conversion:
@@ -141,34 +179,23 @@
 // from an actual 0 converted (if you care about the distinction you need
 // to do more validation before calling this)

-int
-jsmn_atoi(const jsmntok_t *tp, const char *buf)
-{
-    int rv = 0;
+int jsmn_atoi(const jsmntok_t *tp, const char *buf)
+{
     int sign = 1;
-
-    for (int i = tp->start; i < tp->end; i++) {
-        char c = buf[i];
-
-        if (isdigit(c)) {
-            rv *= 10;
-            rv += (c - '0');
-        } else if (i > tp->start) {
-            // non-digit and not first character
-            break;
-        } else {
-            // non-digit and first character, allow '+' or '-'
-            if (c == '-')
-                sign = -1;
-            else if (c != '+')    // i.e., ignore '+', fail all others
-                break;
-        }
-
-    }
-
-    rv *= sign;
-    return rv;
-}
+    jsmntok_t xtp = *tp;
+
+    char first_c = buf[tp->start];
+    if (first_c == '-' || first_c == '+') {
+        if (first_c == '-')
+            sign = -1;
+        xtp.start++;
+    }
+
+    // numbers too big are silently truncated / undefined
+    return sign * (int) jsmn_atoul(&xtp, buf);
+}
+
+

 // Token skip:
@@ -183,8 +210,7 @@
 // by skipping it you've reached the end of the token list)
 //

-int
-jsmn_skiptok(const jsmntok_t *toks)
+int jsmn_skiptok(const jsmntok_t *toks)
 {
     int nskipped = 1;   // i.e. we already know we're skipping THIS one
     int n;
@@ -196,14 +222,14 @@
     // and recursively skip them.

     if (toks->type == JSMN_ARRAY) {
-	for (n = toks->size; n > 0; n--)
+        for (n = toks->size; n > 0; n--)
             nskipped += jsmn_skiptok(toks+nskipped);
     } else if (toks->type == JSMN_OBJECT) {
         // Subtle: the key is always just one token (an object key cannot
         // be an array or another object). So we can skip that one with
         // just a +1, and then recursively skip the value of the key
         // (positioning to that is the other +1)
-	for (n = toks->size; n > 0; n--)
+        for (n = toks->size; n > 0; n--)
             nskipped += 1 + jsmn_skiptok(toks+nskipped+1); // key and value
     }

@@ -219,20 +245,19 @@
 // Return the index (relative to objtp) of the value if found, or -1
 //

-int
-jsmn_findkeyval(jsmntok_t *objtp, const char *buf, const char *k)
+int jsmn_findkeyval(jsmntok_t *objtp, const char *buf, const char *k)
 {
     int nskipped = 1;

     if (objtp->type == JSMN_OBJECT) {
-	for (int n = objtp->size; n > 0; n--) {
+        for (int n = objtp->size; n > 0; n--) {
             jsmntok_t *tp = objtp+nskipped;

             if (jsmn_strcmp(tp, k, buf) == 0)
-		return nskipped+1;
+                return nskipped+1;
             else
                 nskipped += 1 + jsmn_skiptok(tp+1);   // skip key and value
-	}
+        }
     }
     return -1;
 }
@@ -240,6 +265,7 @@
 // -----------------------
 // HTTP/Ethernet utilities
 // -----------------------
+

 // get a character
@@ -256,8 +282,7 @@
 #define HTTPPARSE_TIMEOUT_SECONDS    20L
 #define HTTPPARSE_TIMEOUT            (HTTPPARSE_TIMEOUT_SECONDS * 1000L)

-int
-clientgetc(EthernetClient &ec, unsigned long t0)
+int clientgetc(EthernetClient &ec, unsigned long t0)
 {
     // NOTE that this is in fact a busy loop, but in practice there should
     //      already be data when we are here. In any case it's not like we
@@ -273,8 +298,7 @@
 // get n characters from the client
 // buf must be n+1 long (NUL added, which may or may not make sense but
 // it is always added at the end nevertheless)
-int
-clientgetn(EthernetClient &ec, char *buf, int n, unsigned long t0)
+int clientgetn(EthernetClient &ec, char *buf, int n, unsigned long t0)
 {
     for ( ; n > 0 ; --n) {
         int i = clientgetc(ec, t0);
@@ -288,7 +312,6 @@
     return 0;
 }

-
 // eatline - consume characters until LF
 //
 // Strictly speaking we should be looking for CRLF but
@@ -297,8 +320,7 @@
 // Returns -1 if there was a problem (timeout, connection lost, etc)
 //

-int
-eatline(EthernetClient &ec, unsigned long t0)
+int eatline(EthernetClient &ec, unsigned long t0)
 {
     for(;;) {
         int i;
@@ -314,8 +336,7 @@
 //
 // Returns the first non-space character or -1 for timeouts/disconnect/etc

-int
-skipwhite(EthernetClient &ec, unsigned long t0)
+int skipwhite(EthernetClient &ec, unsigned long t0)
 {
     for(;;) {
         int i;
@@ -326,8 +347,6 @@
             return i;
      }
 }
-
-

 // ------------
 // HTTP Parsing
@@ -367,11 +386,9 @@
 struct urlfuncs {
     char const *url;
     int whatop;
+    int trailing_allowed;
     void (* f)(struct http_rq *, struct urlfuncs *);
 };
-
-
-

@@ -392,8 +409,7 @@
 // Returns zero for success.
 //

-int
-parsefirstline(struct http_rq *rp)
+int parsefirstline(struct http_rq *rp)
 {
     char const *qq;
     char c;
@@ -465,8 +481,7 @@
 //
 // All other headers starting with C are ignored (as are all other headers)
 //
-int
-tryCLen(struct http_rq *rp)
+int tryCLen(struct http_rq *rp)
 {
     const char *p = "ONTENT-LENGTH:";
     int i;
@@ -523,8 +538,7 @@
 // any string not fitting within bufsiz is just truncated
 // Make your buffer big enough for the GET string length you care about
 //
-int
-parseHTTPheader(struct http_rq *rp)
+int parseHTTPheader(struct http_rq *rp)
 {
     int i;
     char c;
@@ -564,13 +578,41 @@
     }
 }

+//
+// Because program space and data space are separate, string literals use
+// RAM which we don't have very much of. This function along with the
+// PROGMEM directive (see Arduino docs) allows string literals to reside
+// in program space (flash) instead of using the limited RAM space for them.
+//
+void serveStringFromPROGMEM(EthernetClient& client, const char *PROGMEMSTR) {
+#define PCOPYSIZE 20          // we do it a little at a time
+    char pcopybuf[PCOPYSIZE];
+
+    int n = strlen_P(PROGMEMSTR);
+    int pcpos = 0;
+
+    for (int i = 0; i < n; i++) {
+        pcopybuf[pcpos++] = pgm_read_byte(PROGMEMSTR+i);
+        if (pcpos == PCOPYSIZE-1 || i == (n-1)) {
+            pcopybuf[pcpos] = '\0';
+            client.print(pcopybuf);
+            pcpos = 0;
+        }
+    }
+}
+
 // basic successful reply (in some cases followed by JSON data)
-const char httpreply1[] = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n";
+const char httpreply1[] PROGMEM =
+    "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n";

 // basic unsuccessful reply
-const char reply400[] =
+const char reply400[] PROGMEM =
     "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n";

+
+
+
+#ifdef DIGITAL_IO

 // reading pins and producing JSON
 //
@@ -589,8 +631,7 @@
 // The above general format is used even if there is just one pin to report.
 // The pin/value object is always inside an array inside "pins".
 //
-void
-processjsonreads(EthernetClient &ec,
+void processjsonreads(EthernetClient &ec,
                  jsmntok_t *tp,
                  char *bufp,
                  int (*readerfunc)(uint8_t))
@@ -605,10 +646,10 @@
     }

     // blurt out the preamble...
-    ec.print(httpreply1);
-    ec.print("{\"serverVersion\":\"");
-    ec.print(revision);
-    ec.print("\",\"pins\":[");
+    serveStringFromPROGMEM(ec, httpreply1);
+    ec.print("{");
+    serveStringFromPROGMEM(ec, revision_json);
+    ec.print(",\"pins\":[");

     // walk through the JSON, reading and blurting more out as we go
     int needcomma = 0;
@@ -642,8 +683,7 @@
 // the pin number, take it as gospel. No sanity is enforced, you get what
 // you ask for whether that makes sense or not.
 //
-void
-do_one_read(struct http_rq *rp, struct urlfuncs *ufp, int (*rf)(uint8_t))
+void do_one_read(struct http_rq *rp, struct urlfuncs *ufp, int (*rf)(uint8_t))
 {
     // we just fake up a JSON request... a little hokey but here we go
     jsmntok_t token;
@@ -656,14 +696,12 @@
     processjsonreads(rp->client, &token, rp->bufp, rf);
 }

-void
-do_one_Aread(struct http_rq *rp, struct urlfuncs *ufp)
+void do_one_Aread(struct http_rq *rp, struct urlfuncs *ufp)
 {
     do_one_read(rp, ufp, analogRead);
 }

-void
-do_one_Dread(struct http_rq *rp, struct urlfuncs *ufp)
+void do_one_Dread(struct http_rq *rp, struct urlfuncs *ufp)
 {
     do_one_read(rp, ufp, digitalRead);
 }
@@ -687,8 +725,8 @@
 // and the client must parse the JSON accordingly (there are other elements
 // in the returned object, e.g., serverVersion).
 //
-void
-do_json_reads(struct http_rq *rp, struct urlfuncs *ufp, int (*rf)(uint8_t))
+void do_json_reads(struct http_rq *rp,
+                   struct urlfuncs *ufp, int (*rf)(uint8_t))
 {
     int i;
     int n;
@@ -718,30 +756,28 @@
     processjsonreads(rp->client, &tokens[i], rp->bufp, rf);
 }

-void
-do_json_Areads(struct http_rq *rp, struct urlfuncs *ufp)
+void do_json_Areads(struct http_rq *rp, struct urlfuncs *ufp)
 {
     do_json_reads(rp, ufp, analogRead);
 }

-void
-do_json_Dreads(struct http_rq *rp, struct urlfuncs *ufp)
+void do_json_Dreads(struct http_rq *rp, struct urlfuncs *ufp)
 {
     do_json_reads(rp, ufp, digitalRead);
 }
+#endif // DIGITAL_IO

 // GET handler for status
 //
 // report on various status items
 // Response is a simple JSON object of various key/value items
 //
-void
-do_status(struct http_rq *rp, struct urlfuncs *ufp)
-{
-    rp->client.print(httpreply1);
-    rp->client.print("{\"serverVersion\":\"");
-    rp->client.print(revision);
-    rp->client.print("\",\"requestsProcessed\":");
+void do_status(struct http_rq *rp, struct urlfuncs *ufp)
+{
+    serveStringFromPROGMEM(rp->client, httpreply1);
+    rp->client.print("{");
+    serveStringFromPROGMEM(rp->client, revision_json);
+    rp->client.print(",\"requestsProcessed\":");
     rp->client.print(requests_processed);
     rp->client.print(",\"uptime_msecs\":");
     rp->client.print(millis());
@@ -767,6 +803,9 @@
 // provided a call-specific substitution table (e.g., "OUTPUT" vs "HIGH")
 // but instead we just allow all the substitutions on any API even though
 // the "wrong" ones probably don't make sense.
+//
+// NOTE that since pins can be configured for BUSY in IR_SEND, this
+// code is included even without DIGITAL_IO
 //

 struct pinop_params {
@@ -779,8 +818,7 @@
 };

-int
-pinops(struct pinop_params *parms)
+int pinops(struct pinop_params *parms)
 {
     int n;
     int i;
@@ -817,6 +855,10 @@
             if (tpm->type == JSMN_STRING) {
                 if (jsmn_strcmp(tpm, "OUTPUT", parms->tokbuf) == 0)
                     v2 = OUTPUT;
+                else if (jsmn_strcmp(tpm, "BUSY", parms->tokbuf) == 0) {
+                    v2 = OUTPUT;
+                    busy_pin = v1;
+                }
                 else if (jsmn_strcmp(tpm, "INPUT", parms->tokbuf) == 0)
                     v2 = INPUT;
                 else if (jsmn_strcmp(tpm, "INPUT_PULLUP", parms->tokbuf) == 0)
@@ -838,8 +880,7 @@
 }

 // the callback for pinmode interface
-void
-call_pinmode(int pin, int mode, struct pinop_params *ignored)
+void call_pinmode(int pin, int mode, struct pinop_params *ignored)
 {
     pinMode(pin, mode);
 }
@@ -855,11 +896,13 @@
 // The second form allows multiple pinmodes per single HTTP operation
 //
 // We accept numbers for mode or: "OUTPUT", "INPUT", "INPUT_PULLUP"
+// As a special case (and this is clearly a hack), mode "BUSY" designates
+// the pin as an OUTPUT that will be automatically set HIGH during
+// request processing (otherwise LOW). Only the last such pin is remembered.
 //
 // Return is just an empty 200 / OK
 //
-void
-do_pinmode(struct http_rq *rp, struct urlfuncs *ufp)
+void do_pinmode(struct http_rq *rp, struct urlfuncs *ufp)
 {
     // we're done with the URL itself so note that we're re-using
     // that buffer for the JSON in the POST now. Yay small footprints :)
@@ -878,14 +921,14 @@
     px.key2 = "mode";

     if (pinops(&px) == 0)
-        rp->client.print(httpreply1);
+        serveStringFromPROGMEM(rp->client, httpreply1);
     else
-        rp->client.print(reply400);
-}
-
+        serveStringFromPROGMEM(rp->client, reply400);
+}
+
+#ifdef DIGITAL_IO
 // the callback for digitalWrite interface
-void
-call_digwrite(int pin, int val, struct pinop_params *ignored)
+void call_digwrite(int pin, int val, struct pinop_params *ignored)
 {
     digitalWrite(pin, val);
 }
@@ -904,10 +947,7 @@
 //
 // Return is just an empty 200 / OK

-
-
-void
-do_digitalwrite(struct http_rq *rp, struct urlfuncs *ufp)
+void do_digitalwrite(struct http_rq *rp, struct urlfuncs *ufp)
 {
     // we're done with the URL itself so note that we're re-using
     // that buffer for the JSON in the POST now. Yay small footprints :)
@@ -926,16 +966,166 @@
     px.key2 = "value";

     if (pinops(&px) == 0)
-        rp->client.print(httpreply1);
+        serveStringFromPROGMEM(rp->client, httpreply1);
     else
+        serveStringFromPROGMEM(rp->client, reply400);
+}
+#endif // DIGITAL_IO
+
+#ifdef IR_SEND
+// parse the POST parameters for an ir send
+// The allowed forms are:
+//      {"codes" : { code-dict }}
+//   or {"codes" : [ { code-dict1 ], {code-dict2} , ... ]}
+//
+// where each code-dict is something like:
+//     { "code": 12345, "bits": 32, "protocol": "NEC", "delay": 250000 }
+//
+// At the top level (same as "codes") you can also add a "repeat": nnn.
+// BE VERY CAREFUL WITH THIS BECAUSE LONG REPEATS WILL MAKE THE SERVER
+// COMPLETELY UNRESPONSIVE (it won't complete the HTTP request until
+// the repeat is done). The "repeat" will operate as if you posted
+// the entire request (sans "repeat") N times.
+//
+// To save buffer space (make the POST smaller) if you are using the array
+// of code-dict form the bits/protocol and delay from the first one become
+// the default if the subsequent ones do not specify alternate values
+// (and the alternate values then become the defaults for the remaining)
+//
+// Each protocol may also define special encodings for certain codes.
+// For example, the NEC "repeat key" code is represented by:
+//    { "code": 4294967295 }
+// (this is really "-1" but you can't send that). This is an illegal NEC
+// code but will trigger special-purposes logic here to send the "repeated
+// keypress" protocol element.
+//
+
+int irjson(char *bufp)
+{
+    int n;
+    int i;
+    jsmn_parser parser;
+    jsmntok_t tokens[MAXJSMNTOKENS];
+    jsmntok_t *tp = NULL;
+    jsmntok_t *bits_tok = NULL;
+    jsmntok_t *proto_tok = NULL;
+    jsmntok_t *delay_tok = NULL;
+    int ix;
+    int repeat_count = 1;
+
+    jsmn_init(&parser);
+    n = jsmn_parse(&parser, bufp, strlen(bufp), tokens, MAXJSMNTOKENS);
+    if (n < 1)
+        return -1;
+
+    i = jsmn_findkeyval(tokens, bufp, "repeat");
+    if (i != -1)
+        repeat_count = jsmn_atoi(&tokens[i], bufp);
+
+
+    for (; repeat_count > 0; repeat_count--) {
+
+        i = jsmn_findkeyval(tokens, bufp, "codes");
+        if (i == -1)
+            return -1;
+        tp = &tokens[i];
+        if (tp->type == JSMN_ARRAY) {
+            n = tp->size;             // number of contained settings
+            tp++;                     // skip array itself (i.e., go to first)
+        } else if (tp->type == JSMN_OBJECT) {
+            n = 1;                    // directly specified object
+        } else {
+            return -1;                // you have screwed up
+        }
+
+        for (; n > 0; n--) {
+            ix = jsmn_findkeyval(tp, bufp, "code");
+            // silently ignore entries without a code
+            if (ix != -1) {
+                unsigned long code = jsmn_atoul(&tp[ix], bufp);
+                unsigned long delay_usec;
+                int bits;
+
+            // find/accept any overrides
+                ix = jsmn_findkeyval(tp, bufp, "bits");
+                if (ix != -1)
+                    bits_tok = &tp[ix];
+            if (bits_tok)
+                bits = jsmn_atoi(bits_tok, bufp);
+            else
+                bits = 32;
+
+                ix = jsmn_findkeyval(tp, bufp, "delay");
+                if (ix != -1)
+                    delay_tok = &tp[ix];
+            if (delay_tok)
+                delay_usec = jsmn_atoul(delay_tok, bufp);
+            else
+                delay_usec = 150000L;
+
+                ix = jsmn_findkeyval(tp, bufp, "protocol");
+                if (ix != -1)
+                    proto_tok = &tp[ix];
+
+                if (proto_tok) {
+                    if (proto_tok->type == JSMN_STRING) {
+                        if (jsmn_strcmp(proto_tok, "NEC", bufp) == 0) {
+                        if (code == 4294967295) {
+                            unsigned int rawb[3];
+                            rawb[0] = 9000;  // Mark 9ms
+                                rawb[1] = 2250;  // Space 2.25ms
+                                rawb[2] = 560;   // Burst
+                            irsend.sendRaw(rawb, 3, 38);
+                        } else {
+                            irsend.sendNEC(code, bits);
+                            }
+                        } else if (jsmn_strcmp(proto_tok, "SONY", bufp) == 0)
+                            irsend.sendSony(code, bits);
+                        else if (jsmn_strcmp(proto_tok, "RC5", bufp) == 0)
+                            irsend.sendRC5(code, bits);
+                        else if (jsmn_strcmp(proto_tok, "RC6", bufp) == 0)
+                            irsend.sendRC6(code, bits);
+                        // self evident how to add more. Note that some of
+                        // the protocols have other fields and you will need to
+                        // add more JSON (and parsing) for those
+                        // XXX requests falling through here are ignored silently
+                    }
+                }
+            if (delay_usec < 10000)
+                delayMicroseconds((int)delay_usec);
+            else
+                delay(delay_usec/1000);
+                tp += jsmn_skiptok(tp);
+            }
+        }
+    }
+    return 0;
+}
+
+
+void do_json_send_ir(struct http_rq *rp, struct urlfuncs *ufp)
+{
+    // we're done with the URL itself so note that we're re-using
+    // that buffer for the JSON in the POST now. Yay small footprints :)
+    if (rp->content_length > rp->bufsiz-1 ||
+        clientgetn(rp->client, rp->bufp, rp->content_length, rp->t0) == -1) {
         rp->client.print(reply400);
-}
-
-void
-send_badrequest(struct http_rq *rp, struct urlfuncs *ufp)
-{
-    rp->client.print(reply400);
-}
+        return;
+    }
+
+    if (irjson(rp->bufp) == 0)
+        serveStringFromPROGMEM(rp->client, httpreply1);
+    else
+        serveStringFromPROGMEM(rp->client, reply400);
+}
+#endif
+
+
+void send_badrequest(struct http_rq *rp, struct urlfuncs *ufp)
+{
+    serveStringFromPROGMEM(rp->client, reply400);
+}
+

 // -----------
 // URL scheme
@@ -965,27 +1155,64 @@
 // GET /v1/status
 //    Return miscellaneous debug/status info object
 //
-// Note that the trailing slash on some URLs is not an accident or
-// superfluous; that tells the dispatch matcher whether or not the
-// URL is a "complete" URL (nothing added at end) or whether there
-// will be extra operation-specific stuff (in the URL) after that point.
-//
-struct urlfuncs dt[] =
-      { { "/v1/analogRead/", HTTPPARSE_GET, do_one_Aread },
-        { "/v1/analogRead", HTTPPARSE_POST, do_json_Areads },
-        { "/v1/digitalRead/", HTTPPARSE_GET, do_one_Dread },
-        { "/v1/digitalRead", HTTPPARSE_POST, do_json_Dreads },
-        { "/v1/digitalWrite", HTTPPARSE_POST, do_digitalwrite },
-        { "/v1/configure/pinmode", HTTPPARSE_POST, do_pinmode },
-        { "/v1/status", HTTPPARSE_GET, do_status },
-        { NULL, HTTPPARSE_NONE, send_badrequest }};
+// POST /v1/sendIR
+//    Sends IR codes using the IRremote library
+
+
+const char P_anaRead[] PROGMEM = "/v1/analogRead";
+const char P_digRead[] PROGMEM = "/v1/digitalRead";
+const char P_digWrite[] PROGMEM = "/v1/digitalWrite";
+const char P_confpin[] PROGMEM = "/v1/configure/pinmode";
+const char P_sendIR[] PROGMEM = "/v1/sendIR";
+const char P_recvIR[] PROGMEM = "/v1/recvIR";
+const char P_status[] PROGMEM = "/v1/status";
+
+#define TRAILING_OK 1
+#define NO_TRAILING 0
+struct urlfuncs dt[] = {
+#ifdef DIGITAL_IO
+        { P_anaRead,  HTTPPARSE_GET,  TRAILING_OK, do_one_Aread },
+        { P_anaRead,  HTTPPARSE_POST, NO_TRAILING, do_json_Areads },
+        { P_digRead,  HTTPPARSE_GET,  TRAILING_OK, do_one_Dread },
+        { P_digRead,  HTTPPARSE_POST, NO_TRAILING, do_json_Dreads },
+        { P_digWrite, HTTPPARSE_POST, NO_TRAILING, do_digitalwrite },
+#endif // DIGITAL_IO
+#ifdef IR_SEND
+        { P_sendIR,   HTTPPARSE_POST, NO_TRAILING, do_json_send_ir },
+#endif
+#ifdef IR_RECV
+        { P_recvIR,   HTTPPARSE_GET,  NO_TRAILING, do_json_read_ir },
+#endif
+        { P_status,   HTTPPARSE_GET,  NO_TRAILING, do_status },
+        { P_confpin,  HTTPPARSE_POST, NO_TRAILING, do_pinmode },
+        { NULL,       HTTPPARSE_NONE, NO_TRAILING, send_badrequest }
+};
+
+int dtpmatcher(struct urlfuncs *dtp, struct http_rq *rqp) {
+    if (dtp->whatop != rqp->operation)
+        return 0;
+
+    char *s = rqp->bufp;
+    int n = strlen_P(dtp->url);
+
+    for (int i = 0; i < n; i++) {
+        if (pgm_read_byte(dtp->url+i) != s[i])
+            return 0;
+    }
+
+    // we got all the way to the end of the dtp url. If we are not at
+    // the end of the string s then two things must be true:
+    //      dtp->trailing_allowed must be true
+    //      the next character in s must be '/'
+
+    return (s[n] == '\0') || (dtp->trailing_allowed && s[n] == '/');
+}

 //
 // called by the main loop to check and process web requests periodically
 //
-void
-webProcessing(EthernetClient& ec)
+void webProcessing(EthernetClient& ec)
 {
     //
     //
@@ -1007,20 +1234,8 @@
         struct urlfuncs *dtp;

         for (dtp = dt; dtp->url; dtp++) {
-            if (dtp->whatop == rq.operation) {
-
-                // if the URL pattern ends with '/' we only look at leading
-                // equivalence (strncmp). This allows for extra info after
-                // the endpoint. Otherwise the endpoint must match exactly.
-            	int n = strlen(dtp->url);
-            	if (dtp->url[n-1] == '/') {
-            	    if (strncmp(dtp->url, sbuf, n) == 0)
-            	        break;
-            	} else {
-            	    if (strcmp(dtp->url, sbuf) == 0)
-            	        break;
-                }
-            }
+            if (dtpmatcher(dtp, &rq))
+                break;
         }
         (* dtp->f)(&rq, dtp);
         ++requests_processed;
@@ -1048,7 +1263,11 @@
     // check for web requests and do them
     EthernetClient client = server.available();
     if (client) {
+       if (busy_pin >= 0)
+           digitalWrite(busy_pin, HIGH);
        webProcessing(client);
        client.stop();
-    }
-}
+       if (busy_pin >= 0)
+           digitalWrite(busy_pin, LOW);
+    }
+}


Angular constraints for all servos investigated. Need to make them programatic instead of constants, as the boom arm and main arm constraints depend on eachother.

--- Project/demo_just_get_it_working/demo_just_get_it_working.ino
+++ Project/demo_just_get_it_working/demo_just_get_it_working.ino
@@ -16,7 +16,8 @@
 #include <Servo.h>  // servo library

 Servo servo[4];  // servo control objects
-int angles[4], speeds[4];
+int angles[4] = { 90, 65, 90, 60 };   // initial angles
+int constraints[4][2] = { {0, 180}, {65, 125}, {65, 140}, {60, 150} };

 void setup()
 {
@@ -24,41 +25,40 @@
   servo[1].attach( 6 );
   servo[2].attach( 5 );
   servo[3].attach( 3 );
-
-  int n;
-  for ( n=0; n<4; n++ )
-  {
-    servo[n].write( 90 );
-  }
+  for ( int n=0; n<4; n++ ) { servo[n].write( angles[n] ); }

   Serial.begin(9600);
-
-  pinMode(13, OUTPUT);
 }

 void loop()
 {
-  int n;
-  for ( n=0; n<4; n++ )
+  Serial.println();
+
+  Serial.print("Servo #");
+  while ( Serial.available() == 0 ) { }
+  int servo_num = Serial.parseInt();
+  Serial.println( servo_num );
+
+  Serial.print("Angle: ");
+  while ( Serial.available() == 0 ) { }
+  int angle = Serial.parseInt();
+  Serial.println( angle );
+
+  angle = constrain(angle, constraints[servo_num][0], constraints[servo_num][1]);
+
+  Serial.print("Setting servo #");
+  Serial.print( servo_num );
+  Serial.print(" to ");
+  Serial.print( angle );
+  Serial.println(" degrees.");
+
+  int dir = (angle - angles[servo_num]) / abs(angle - angles[servo_num]);
+  while ( angles[servo_num] != angle )
   {
-    Serial.print("\nEnter new angle for servo ");
-    Serial.write('#');
-    Serial.print( n );
-    Serial.print(": ");
-
-    while ( Serial.available() == 0 ) { }
-
-    angles[n] = Serial.parseInt();
-    Serial.print( angles[n] );
-    Serial.println(" deg entered.");
-
-    angles[n] = constrain( angles[n], 0, 180 );
-
-    Serial.print("Setting angle to ");
-    Serial.print( angles[n] );
-    Serial.println(" deg.");
-
-    servo[n].write( angles[n] );
+    angles[servo_num] += dir;
+    servo[servo_num].write( angles[servo_num] );
+    delay(50);
+    Serial.write('.');
   }
 }


Move the frames to a const double array so that we can index them via the frame number and so that it is stored in code space and not on the stack. This changes the index to zero based, so the frames from the remote have to change

--- projects/arduino/led_matrix_256/led_matrix_256.ino
+++ projects/arduino/led_matrix_256/led_matrix_256.ino
@@ -16,7 +16,6 @@
 //int sensorPin = A5;    // select the input pin for the potentiometer
 //int sensorValue = 0;  // variable to store the value coming from the sensor

-int frame;
 int j;
 uint32_t i;
 uint8_t d = 0;
@@ -29,142 +28,103 @@
 uint8_t w = b+g+r;

+
+const uint8_t frames[][NUM_OF_PIXELS] = { {
+        d,d,w,w,d,d,d,d,d,d,d,d,d,d,d,d,
+        d,d,d,d,d,d,d,d,d,d,d,w,d,w,d,d,
+        d,d,d,w,d,w,d,d,d,d,d,d,d,d,d,d,
+        d,d,d,d,d,d,d,d,d,w,d,d,d,d,d,d,
+        d,d,d,d,d,d,w,w,w,w,w,d,d,d,d,d,
+        d,d,d,d,d,w,w,w,w,w,d,d,d,d,d,d,
+        d,d,d,d,d,r,r,r,g,r,r,r,d,d,d,d,
+        d,d,d,r,r,r,r,g,r,r,r,r,d,d,d,d,
+        d,d,d,r,r,r,r,g,g,g,r,r,r,r,d,d,
+        d,d,r,r,r,r,g,g,g,r,r,r,r,d,d,d,
+        d,d,d,r,r,r,g,g,g,g,g,r,r,r,d,d,
+        d,d,r,r,g,g,g,g,g,g,g,r,r,d,d,d,
+        d,d,d,r,g,g,g,g,g,g,g,g,g,r,d,d,
+        d,d,d,r,r,r,r,y,r,r,r,r,d,d,d,d,
+        d,d,d,d,d,r,r,r,y,r,r,r,d,d,d,d,
+        d,d,d,d,d,r,r,y,r,r,d,d,d,d,d,d
+    },
+
+    {   d,d,d,d,d,d,d,d,r,d,d,d,d,d,d,d,
+        d,d,d,d,d,d,d,r,d,d,d,d,d,d,d,d,
+        d,d,d,d,d,d,d,r,w,r,d,d,d,d,d,d,
+        d,d,d,d,d,r,r,w,r,r,d,d,d,d,d,d,
+        d,d,d,d,d,d,d,r,r,r,d,d,d,d,d,d,
+        d,d,d,d,d,d,d,y,d,d,d,d,d,d,d,d,
+        d,d,d,d,d,d,d,d,y,d,d,d,d,d,d,d,
+        d,d,d,d,w,w,w,w,w,w,w,d,d,d,d,d,
+        d,d,d,d,d,w,w,w,w,w,w,w,d,d,d,d,
+        d,d,d,d,w,w,w,w,w,w,w,d,d,d,d,d,
+        d,d,d,d,d,w,w,w,w,w,w,w,d,d,y,d,
+        y,d,y,d,w,w,w,w,w,w,w,d,d,d,d,d,
+        y,y,y,d,d,w,w,w,w,w,w,w,d,d,y,d,
+        d,y,y,y,w,w,w,w,w,w,w,y,y,y,y,d,
+        d,d,d,y,y,y,y,y,y,y,y,y,y,y,d,d,
+        d,d,d,y,y,y,y,y,y,y,y,y,d,d,d,d
+    },
+
+    {   d,d,d,d,d,d,d,g,g,d,d,d,d,d,d,d,
+        d,d,d,d,d,d,d,g,g,d,d,d,d,d,d,d,
+        d,d,d,d,d,d,b,g,g,g,d,d,d,d,d,d,
+        d,d,d,d,d,d,g,g,b,r,d,d,d,d,d,d,
+        d,d,d,d,d,g,g,g,b,g,b,d,d,d,d,d,
+        d,d,d,d,d,g,b,g,g,g,b,d,d,d,d,d,
+        d,d,d,d,g,g,b,g,g,g,b,b,d,d,d,d,
+        d,d,d,d,g,r,g,b,b,g,g,g,d,d,d,d,
+        d,d,d,g,b,g,g,g,g,b,b,g,g,d,d,d,
+        d,d,d,g,b,p,g,g,g,b,b,g,g,d,d,d,
+        d,d,b,g,p,g,g,b,g,g,g,g,b,b,d,d,
+        d,d,g,g,y,g,b,b,g,r,g,b,b,g,d,d,
+        d,g,g,g,g,b,b,g,g,g,b,b,g,g,g,d,
+        d,d,d,d,d,d,y,y,y,y,d,d,d,d,d,d,
+        d,d,d,d,d,d,y,y,y,y,d,d,d,d,d,d,
+        d,d,d,d,d,d,y,y,y,y,d,d,d,d,d,d
+    }
+};
+
+const uint16_t MAX_FRAME = sizeof(frames) / NUM_OF_PIXELS;
+
+
 void setup() {
     irrecv.enableIRIn(); // Start the remote control receiver

     Serial.begin(9600); // setup serial printing so you can troubleshoot

     strip.begin();
+    strip.setBrightness(1);
     strip.show(); // Initialize all pixels to 'off'

 }

 void loop() {
+    uint16_t frameNumber = myRemoteRead();
+    Serial.print("Frame in Show Routine=");
+    Serial.print(frameNumber);
+    Serial.println();

-    //colorWipe(strip.Color(255, 0, 0), 10); // Red and time (not to exceed 255 since uint8_t is used below) to next LED
-    //rainbow(1);
-
-
-    Serial.print("Frame in loop=");
-    Serial.print(frame);
-    Serial.println();
+    renderFrame(frameNumber);
     delay(200);
-    //sensorValue = analogRead(sensorPin);  //read the analog input value and print it
-    //int frame = sensorValue/250;
-
-    rainbowCycle(128);
-    //colorWipe1(strip.Color(0, 0, 255), 10); // Red and time to next LED
 }

+void renderFrame( uint16_t frameNumber ) {
+  // Ensure that there is a valid frame, otherwise we will walk off the memory allocated and then bad things happen
+  if (frameNumber >= 0 && frameNumber <= MAX_FRAME) {

-// Slightly different, this makes the rainbow equally distributed throughout
-void rainbowCycle(uint8_t wait) {
-    frame=myRemoteRead();
-    Serial.print("Frame in Show Routine=");
-    Serial.print(frame);
-    Serial.println();
-//Frame 1
-    if (frame==1) {
-
-        uint8_t myMatrixRow1[256]  = {d,d,w,w,d,d,d,d,d,d,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,d,d,d,d,d,w,d,w,d,d,
-                                      d,d,d,w,d,w,d,d,d,d,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,d,d,d,w,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,w,w,w,w,w,d,d,d,d,d,
-                                      d,d,d,d,d,w,w,w,w,w,d,d,d,d,d,d,
-                                      d,d,d,d,d,r,r,r,g,r,r,r,d,d,d,d,
-                                      d,d,d,r,r,r,r,g,r,r,r,r,d,d,d,d,
-                                      d,d,d,r,r,r,r,g,g,g,r,r,r,r,d,d,
-                                      d,d,r,r,r,r,g,g,g,r,r,r,r,d,d,d,
-                                      d,d,d,r,r,r,g,g,g,g,g,r,r,r,d,d,
-                                      d,d,r,r,g,g,g,g,g,g,g,r,r,d,d,d,
-                                      d,d,d,r,g,g,g,g,g,g,g,g,g,r,d,d,
-                                      d,d,d,r,r,r,r,y,r,r,r,r,d,d,d,d,
-                                      d,d,d,d,d,r,r,r,y,r,r,r,d,d,d,d,
-                                      d,d,d,d,d,r,r,y,r,r,d,d,d,d,d,d
-                                     };
-
-        for(i=0; i< strip.numPixels(); i++) {
-            strip.setPixelColor(i, myMatrixRow1[i]);
-            strip.setBrightness(1);
-            //delay(50);
-        }
-
-
-        //delay(4000);
+    // Setup every pixels
+    for(uint16_t i = 0; i < strip.numPixels(); i++) {
+        strip.setPixelColor(i, frames[frameNumber][i]);
     }

-//Frame 2
-    else if (frame==2) {
-
-        uint8_t myMatrixRow2[256] = {d,d,d,d,d,d,d,d,r,d,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,d,r,d,d,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,d,r,w,r,d,d,d,d,d,d,
-                                      d,d,d,d,d,r,r,w,r,r,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,d,r,r,r,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,d,y,d,d,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,d,d,y,d,d,d,d,d,d,d,
-                                      d,d,d,d,w,w,w,w,w,w,w,d,d,d,d,d,
-                                      d,d,d,d,d,w,w,w,w,w,w,w,d,d,d,d,
-                                      d,d,d,d,w,w,w,w,w,w,w,d,d,d,d,d,
-                                      d,d,d,d,d,w,w,w,w,w,w,w,d,d,y,d,
-                                      y,d,y,d,w,w,w,w,w,w,w,d,d,d,d,d,
-                                      y,y,y,d,d,w,w,w,w,w,w,w,d,d,y,d,
-                                      d,y,y,y,w,w,w,w,w,w,w,y,y,y,y,d,
-                                      d,d,d,y,y,y,y,y,y,y,y,y,y,y,d,d,
-                                      d,d,d,y,y,y,y,y,y,y,y,y,d,d,d,d
-                                     };
-
-        for(i=0; i< strip.numPixels(); i++) {
-            strip.setPixelColor(i, myMatrixRow2[i]);
-            strip.setBrightness(1);
-            //delay(50);
-        }
-
-
-        //delay(1000);
-    }
-
-//Frame 3
-    else if (frame==3) {
-
-        uint8_t myMatrixRow3[256] = {d,d,d,d,d,d,d,g,g,d,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,d,g,g,d,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,b,g,g,g,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,g,g,b,r,d,d,d,d,d,d,
-                                      d,d,d,d,d,g,g,g,b,g,b,d,d,d,d,d,
-                                      d,d,d,d,d,g,b,g,g,g,b,d,d,d,d,d,
-                                      d,d,d,d,g,g,b,g,g,g,b,b,d,d,d,d,
-                                      d,d,d,d,g,r,g,b,b,g,g,g,d,d,d,d,
-                                      d,d,d,g,b,g,g,g,g,b,b,g,g,d,d,d,
-                                      d,d,d,g,b,p,g,g,g,b,b,g,g,d,d,d,
-                                      d,d,b,g,p,g,g,b,g,g,g,g,b,b,d,d,
-                                      d,d,g,g,y,g,b,b,g,r,g,b,b,g,d,d,
-                                      d,g,g,g,g,b,b,g,g,g,b,b,g,g,g,d,
-                                      d,d,d,d,d,d,y,y,y,y,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,y,y,y,y,d,d,d,d,d,d,
-                                      d,d,d,d,d,d,y,y,y,y,d,d,d,d,d,d
-                                     };
-
-        for(i=0; i< strip.numPixels(); i++) {
-            strip.setPixelColor(i, myMatrixRow3[i]);
-            strip.setBrightness(1);
-            //delay(50);
-        }
-
-
-        //delay(1000);
-    }
-    if (frame<=3) {
-        if(frame>=0) {
-            strip.show();
-        }
-    }
-//delay(5000);
+    // Render the frame
+    strip.show();
+  }
 }

-int myRemoteRead() {
-    int remoteframe=3;
+uint16_t myRemoteRead() {
+    uint16_t remoteframe=3;
     Serial.print("remoteframe=");
     Serial.print(remoteframe);
     Serial.println();


arduino.ino: reference who wrote the code, not the team

--- arduino.ino
+++ arduino.ino
@@ -2,15 +2,11 @@
  * Arduindow - Opens and closes the windows in your house
  * using an Arduino and open weather data.
  *
- * Team:
- *     Erica Raviola (iOS)
- *     Flavio Giobergia (APIs)
- *     Roberto Gambotto (Arduino)
- *     Simone Basso (sandwiches)
- *
  * Homepage: <https://github.com/bassosimone/arduindow>.
  *
  * See LICENSE for license conditions.
+ *
+ * Written by Roberto Gambotto and Simone Basso.
  */

 #include <SPI.h>


hard joystick mode wired

hard joystick mode wired

--- Arduino/Thingamabot/Thingamabot.ino
+++ Arduino/Thingamabot/Thingamabot.ino
@@ -169,13 +169,13 @@
   int left_motor_speed = 0;
   if (diff_speed >= 0)
   {
-    right_motor_speed = diff_speed - diff_turn;
-    left_motor_speed = diff_speed + diff_turn;
+    right_motor_speed = diff_speed + diff_turn;
+    left_motor_speed = diff_speed - diff_turn;
   }
   else
   {
-    right_motor_speed = diff_speed + diff_turn;
-    left_motor_speed = diff_speed - diff_turn;
+    right_motor_speed = diff_speed - diff_turn;
+    left_motor_speed = diff_speed + diff_turn;
   }
   set_motor(LEFT_MOTOR, left_motor_speed);
   set_motor(RIGHT_MOTOR, right_motor_speed);


Move some magic numbers into defines

Move some magic numbers into defines

Magic numbers in code shouldn't exist, so they have been moved into defines.
Although magic numbers still exist, they are at least hidden by names that
describe what they do.

This allows easy changes to be made, simply by modifying the defines in one
place instead of having to search through the code and changing multiple
values.

--- Guzheng/Guzheng.ino
+++ Guzheng/Guzheng.ino
@@ -13,30 +13,31 @@
 #define VOLUME 0x7F
 #define DELAY 0

+// MIDI Instrument Array
+#define NUMINSTRUMENTS 6
+
 // MIDI command defines
 #define NOTEON 0x90
 #define NOTEOFF 0x80
 #define CHANGEINSTRUMENT 0xC0
-#define STRING 0x2E
-#define PIANO 0x01

 // Values for different notes
 // Currently a C scale, starting from middle C (C4)
-int note8 = 0x48;
-int note7 = 0x47;
-int note6 = 0x45;
-int note5 = 0x43;
-int note4 = 0x41;
-int note3 = 0x40;
-int note2 = 0x3E;
-int note1 = 0x3C;
+#define note8 0x48
+#define note7 0x47
+#define note6 0x45
+#define note5 0x43
+#define note4 0x41
+#define note3 0x40
+#define note2 0x3E
+#define note1 0x3C

 int buttonState = 0;
 int currentInstrument = 0;

 // Piano, Harpsichord, Pizzacato strings, Flute, Violin, Accordian
 // Sounds provided by MIDI synthesizer, so you may need to install it
-int instrumentArray[6] = {0x01, 0x07, 0x2E, 0x4A, 0x29, 0x16};
+int instrumentArray[NUMINSTRUMENTS] = {0x01, 0x07, 0x2E, 0x4A, 0x29, 0x16};

 /* Flag values for each laser. These values will operate on the bit
  * levels, directly with binary. They work together with the
@@ -151,14 +152,14 @@
         // Send MIDI command to change instrument
         Serial.write(CHANGEINSTRUMENT);
         // Max number of instruments, so wrap around
-        if (currentInstrument >= 5) {
+        if (currentInstrument >= (NUMINSTRUMENTS - 1)) {
           currentInstrument = 0;
         } else {
           // Increment current instrument
           currentInstrument++;
         }
         // Write the current instrument
-        Serial.write(instrumentArray[currentInstrument];
+        Serial.write(instrumentArray[currentInstrument]);
         // Set the button state so this won't fire when holding down
         // the button
         buttonState = 1;


Use maximum frequency

Use maximum frequency

--- HackcenterLicht.ino
+++ HackcenterLicht.ino
@@ -14,7 +14,7 @@
 uint8_t lightValues[NUM_LIGHTS] = { 0 };
 const PROGMEM char text_html[] = "text/html";

-PCA9685 driver = PCA9685(0x00, PCA9685_MODE_N_DRIVER);
+PCA9685 driver = PCA9685(0x00, PCA9685_MODE_N_DRIVER, PCA9685_MAX_FREQUENCY);
 ESP8266WebServer server(80);

 uint8_t mapLightAddress(uint8_t light) {


removed readCanMessages

removed readCanMessages

--- Firmware/Firmware.ino
+++ Firmware/Firmware.ino
@@ -5,64 +5,14 @@
 // /_/  /_/\___/\___/\__,_/_/ /_/\__,_/_/ /_/ /_/
 //

-#include <CAN.h>
-#include <SPI.h>
-
 void setup()
 {
     Serial.begin(115200);
-    api_init();
-    robot_init();
+    api_begin();
+    robot_begin();
 }

 void loop()
 {
     api_update();
-    // readCanMessages();
 }
-
-/**
- * Reads all incoming CAN-Messages and dumps them on the serial port
- */
-void readCanMessages()
-{
-    byte length = 0;
-    byte rx_status = 0;
-    unsigned short frame_id = 0;
-    byte frame_data[8];
-
-    // clear frame data
-    for (int i = 0; i < 8; i++)
-    {
-        frame_data[i] = 0;
-    }
-
-    rx_status = CAN.readStatus();
-    if ((rx_status & 0x01) == 0x01)
-    {
-        CAN.readDATA_ff_0(&length, frame_data, &frame_id);
-    }
-    else if ((rx_status & 0x02) == 0x02)
-    {
-        CAN.readDATA_ff_1(&length, frame_data, &frame_id);
-    }
-
-    if (((rx_status & 0x01) == 0x01) || ((rx_status & 0x02) == 0x02))
-    {
-        Serial.print("Rx:");
-        Serial.print(rx_status, HEX);
-        Serial.print(" ");
-        Serial.print(length, HEX);
-
-        Serial.print(" - ");
-        Serial.print(frame_id, HEX);
-        Serial.print(": ");
-
-        for (int i = 0; i < 8; i++)
-        {
-            Serial.print(" ");
-            Serial.print(frame_data[i], HEX);
-        }
-        Serial.println();
-    }
-}


Delete adxl345multi.ino

Delete adxl345multi.ino

--- adxl345multi.ino
+++ adxl345multi.ino
@@ -1,180 +0,0 @@
-#include "math.h";        // include math library for square root function
-#define Addr 0x53         // ADXL345 I2C address is 0x53(83)
-#define GScaling 0.003901 // 3.9mG per bit A2D Scaling
-#define A2DRES 8192       // 2^13 = 8192
-#define HALFSCALE 4095    // (2^13)/2 - 1
-
-int x0data[32], x1data[32], y0data[32], y1data[32], z0data[32], z1data[32];
-int xdata[32], ydata[32], zdata[32];
-int xos, yos, zos;
-int xavg, yavg, zavg;
-double Adata[8];
-
-int configAcc(String command);  // function declaration for calibration
-
-void setup()
-{
-  // Declare particle variables and function
-  Particle.variable("i2cdevice","ADXL345");
-  Particle.variable("xAvg",xavg);
-  Particle.variable("yAvg",yavg);
-  Particle.variable("ZAvg",zavg);
-  Particle.function("AccSetup", configAcc);
-
-  Wire.begin();                 // Initialise I2C communication as MASTER
-  Serial.begin(38400);          // set baud rate
-
-  Wire.beginTransmission(Addr);
-  Wire.write(0x2D);             // Select power control register
-  Wire.write(0x08);             // disable auto sleep
-  Wire.endTransmission();
-
-  Wire.beginTransmission(Addr);
-  Wire.write(0x31);             // Select data format register
-  Wire.write(0b00001011);       // Select full resolution, +/-16g
-  Wire.endTransmission();
-
-  delay(100);
-}
-
-void loop()
-{
-  for(int f = 0; f < 8; f++)      // step through 8 frequencies starting with ODR = 25Hz
-  {
-    Wire.beginTransmission(Addr); // Start I2C Transmission
-    Wire.write(0x2C);             // Select bandwidth rate register
-    Wire.write(8 + f);            // 25, 50, 100, 200, 400, 800, 1.6k, 3.2k
-    Wire.write(0x38);             // Set FIFO Control register
-    Wire.write(0b01011111);       // FIFO mode
-    Wire.endTransmission();
-    delay(100);
-
-    for(int i = 0; i < 32; i++)       // Read all 32 values from FIFO
-    {
-      Wire.beginTransmission(Addr);
-      Wire.write(0x32);               // Point to the DATAX0 register
-      Wire.requestFrom(Addr,6);       // request 6 bytes of data (X0 - Z1)
-      while(Wire.available())
-      {
-        x0data[i] = Wire.read();      // put x,y,z data into arrays
-        x1data[i] = Wire.read();
-        y0data[i] = Wire.read();
-        y1data[i] = Wire.read();
-        z0data[i] = Wire.read();
-        z1data[i] = Wire.read();
-      }
-      Wire.endTransmission();
-    }
-
-    for (int i=1; i<32; i++)    // read all 32 values from arrays to calculate amplitude
-    {
-      xdata[i] = ((x1data[i]) & 0x1F)<<8 | (x0data[i] & 0xFF);    // combine 5 MSB and 8 LSB into one variable
-      ydata[i] = ((y1data[i]) & 0x1F)<<8 | (y0data[i] & 0xFF);
-      zdata[i] = ((z1data[i]) & 0x1F)<<8 | (z0data[i] & 0xFF);
-
-      if (xdata[i]>HALFSCALE) xdata[i] -= A2DRES;  // convert to signed value
-      if (ydata[i]>HALFSCALE) ydata[i] -= A2DRES;  // convert to signed value
-      if (zdata[i]>HALFSCALE) zdata[i] -= A2DRES;  // convert to signed value
-
-      xdata[i] -= xos;                             // subtract offset
-      ydata[i] -= yos;
-      zdata[i] -= zos;
-
-// calculate amplitude by summing the squares, divide vectors by 100 to keep number manageable
-      Adata[f] += (xdata[i]*xdata[i])/100 + (ydata[i]*ydata[i])/100 + (zdata[i]*zdata[i])/100;
-      xavg += xdata[i];     // add samples for average, this is only useful for calibrating system at rest
-      yavg += ydata[i];
-      zavg += zdata[i];
-    }
-    Adata[f] = sqrt(Adata[f]);  // take the squareroot of the sume of the squares
-    xavg = xavg>>5;             // divide by 32 to get averages for offset
-    yavg = yavg>>5;
-    zavg = zavg>>5;
-  }
-  String devID = System.deviceID();
-
-// publish 8 A(f) values in json format
-  Particle.publish("Adata", "{ \"f1\": \"" + String(Adata[0]) + "\"," +
-   "\"f2\": \"" + String(Adata[1]) + "\"," +
-   "\"f3\": \"" + String(Adata[2]) + "\"," +
-   "\"f4\": \"" + String(Adata[3]) + "\"," +
-   "\"f5\": \"" + String(Adata[4]) + "\"," +
-   "\"f6\": \"" + String(Adata[5]) + "\"," +
-   "\"f7\": \"" + String(Adata[6]) + "\"," +
-   "\"f8\": \"" + String(Adata[7]) + "\" }", PRIVATE);
-
-   String xvalues = String(String(xdata[1]) +
-    ", " + String(xdata[2]) + ", " + String(xdata[3]) +
-    ", " + String(xdata[4]) + ", " + String(xdata[5]) +
-    ", " + String(xdata[6]) + ", " + String(xdata[7]) +
-    ", " + String(xdata[8]) + ", " + String(xdata[9]) +
-    ", " + String(xdata[10]) + ", " + String(xdata[11]) +
-    ", " + String(xdata[12]) + ", " + String(xdata[13]) +
-    ", " + String(xdata[14]) + ", " + String(xdata[15]) +
-    ", " + String(xdata[16]) + ", " + String(xdata[17]) +
-    ", " + String(xdata[18]) + ", " + String(xdata[19]) +
-    ", " + String(xdata[20]) + ", " + String(xdata[21]) +
-    ", " + String(xdata[22]) + ", " + String(xdata[23]) +
-    ", " + String(xdata[24]) + ", " + String(xdata[25]) +
-    ", " + String(xdata[26]) + ", " + String(xdata[27]) +
-    ", " + String(xdata[28]) + ", " + String(xdata[29]) +
-    ", " + String(xdata[30]) + ", " + String(xdata[31]));
-   Particle.publish("xvalues:", xvalues);
-
-   String yvalues = String(String(ydata[1]) +
-    ", " + String(ydata[2]) + ", " + String(ydata[3]) +
-    ", " + String(ydata[4]) + ", " + String(ydata[5]) +
-    ", " + String(ydata[6]) + ", " + String(ydata[7]) +
-    ", " + String(ydata[8]) + ", " + String(ydata[9]) +
-    ", " + String(ydata[10]) + ", " + String(ydata[11]) +
-    ", " + String(ydata[12]) + ", " + String(ydata[13]) +
-    ", " + String(ydata[14]) + ", " + String(ydata[15]) +
-    ", " + String(ydata[16]) + ", " + String(ydata[17]) +
-    ", " + String(ydata[18]) + ", " + String(ydata[19]) +
-    ", " + String(ydata[20]) + ", " + String(ydata[21]) +
-    ", " + String(ydata[22]) + ", " + String(ydata[23]) +
-    ", " + String(ydata[24]) + ", " + String(ydata[25]) +
-    ", " + String(ydata[26]) + ", " + String(ydata[27]) +
-    ", " + String(ydata[28]) + ", " + String(ydata[29]) +
-    ", " + String(ydata[30]) + ", " + String(ydata[31]));
-   Particle.publish("yvalues:", yvalues);
-
-   String zvalues = String(String(zdata[1]) +
-    ", " + String(zdata[2]) + ", " + String(zdata[3]) +
-    ", " + String(zdata[4]) + ", " + String(zdata[5]) +
-    ", " + String(zdata[6]) + ", " + String(zdata[7]) +
-    ", " + String(zdata[8]) + ", " + String(zdata[9]) +
-    ", " + String(zdata[10]) + ", " + String(zdata[11]) +
-    ", " + String(zdata[12]) + ", " + String(zdata[13]) +
-    ", " + String(zdata[14]) + ", " + String(zdata[15]) +
-    ", " + String(zdata[16]) + ", " + String(zdata[17]) +
-    ", " + String(zdata[18]) + ", " + String(zdata[19]) +
-    ", " + String(zdata[20]) + ", " + String(zdata[21]) +
-    ", " + String(zdata[22]) + ", " + String(zdata[23]) +
-    ", " + String(zdata[24]) + ", " + String(zdata[25]) +
-    ", " + String(zdata[26]) + ", " + String(zdata[27]) +
-    ", " + String(zdata[28]) + ", " + String(zdata[29]) +
-    ", " + String(zdata[30]) + ", " + String(zdata[31]));
-   Particle.publish("zvalues:", zvalues);
-
-  delay(60000);                     // wait 1 minute
-}
-
-int configAcc(String command)       // Particle function for accelerometer calibration
-{
-    if (command == "calibrate")     // if function call = calibrate, then offsets = averages
-    {
-      xos = xavg;
-      yos = yavg;
-      zos = zavg;
-      return 1;
-    }
-    else if (command == "reset")    // if function call = reset, then offsets = 0
-    {
-      xos = 0;
-      yos = 0;
-      zos = 0;
-      return 0;
-    }
-    else return -1;
-}


Programming an outrun scheduler will start an outrun right now.  Cancelling an outrun schedule will stop the motor if it's running.

--- ComposterSketch/ComposterSketch.ino
+++ ComposterSketch/ComposterSketch.ino
@@ -223,7 +223,7 @@
         state=DCL;            //Decelerate to stop
       }

-    //Ignore events until motor decels to a stop then become idle
+    //Decelerating:  Ignore all events until motor controller decels motor to a stop, then become idle
     case DCL:
       if (motor.isStopped()) {
         DPRINT("stopped");
@@ -235,11 +235,12 @@
     case B3W:
       if (b3.isReleased()&&b3t.isRunning()) {       //Did user tap B3?
         sked.setStartTime();                        //Set now as the start time & enable the daily composter autoRun
-        state=IDL;
+        doStartMotor();                             //And start an autorun sequence right now
       } else if (b3.isPressed()&&b3t.isExpired()) {  //Did user hold B3?
         audio.doBeep(FREQC);
-        sked.disable();                               //Disable autoRun schedule
-        state=B3W;                                    //Return to IDL later when user releases B3
+        sked.disable();                             //Yes, disable autoRun schedule
+        motor.stop();                               //Begin stopping the motor if it's running
+        state=DCL;                                  //Deceleraterating now
         DPRINT("~A");
       } else if (b3.isReleased()&&b3t.isExpired()) {
         state=IDL;                                    //Released button
@@ -280,7 +281,7 @@

 /**
- * Helper method to start the drum motor in autorunning state
+ * Helper method to start the drum motor and enter the autorunning state
  */
 void doStartMotor() {
         DPRINT("doStartMotor");


Update Sync.ino

Update Sync.ino

--- examples/More/Sync/Sync.ino
+++ examples/More/Sync/Sync.ino
@@ -13,8 +13,8 @@
  * This example code is in public domain.
  *
  **************************************************************
- * You can synchronize the state of widgets with hardware,
- * event if hardware resets or looses connection temporarily
+ * You can synchronize the state of widgets with hardware states,
+ * even if hardware resets or looses connection temporarily
  *
  * Project setup in the Blynk app:
  *   Slider widget (0...100) on V0


Added flesh to the example

Added flesh to the example

--- examples/PingerTest/PingerTest.ino
+++ examples/PingerTest/PingerTest.ino
@@ -30,6 +30,7 @@

 #include <Pinger.h>

+// Send the trigger pulse on pin 2 and wait for a response on pin 3.
 Pinger pinger(2, 3);

 void setup() {
@@ -37,8 +38,14 @@
 }

 void loop() {
+    // The pinger responds in cm
     float cm = pinger.ping();
+
+    // Print it.
     Serial.print(cm);
     Serial.println("cm");
+
+    // Don't do it too often or there will just be too much data
+    // for the human brain to take in.
     delay(100);
 }


Update Bridge.ino

Update Bridge.ino

Better explanation for sending data over the Virtual Pin with Bridge

--- examples/Widgets/Bridge/Bridge.ino
+++ examples/Widgets/Bridge/Bridge.ino
@@ -59,17 +59,33 @@
 }

 BLYNK_CONNECTED() {
-  bridge1.setAuthToken("OtherAuthToken");
+  bridge1.setAuthToken("OtherAuthToken"); // Place the AuthToken of the second hardware here
 }

 static bool value = true;
-void blynkAnotherDevice()
+void blynkAnotherDevice() // Here we will send HIGH or LOW once per second
 {
   // Send value to another device
   if (value) {
-    bridge1.digitalWrite(9, HIGH);
+    bridge1.digitalWrite(9, HIGH);  // Digital Pin 9 on the second board will be set HIGH
+    bridge1.virtualWrite(V5, 1); // Virtual Pin 5 on the second board will be set HIGH
+/////////////////////////////////////////////////////////////////////////////////////////
+// Keep in mind that when performing virtualWrite with Bridge,
+// second board will need to process the incoming command.
+// It can be done by using this handler on the second board:
+//
+//    BLYNK_WRITE(V5)
+//    {
+//    int pinData = param.asInt(); // pinData variable will store value that came via Bridge
+//    }
+//
+/////////////////////////////////////////////////////////////////////////////////////////
+}
+
+
   } else {
-    bridge1.digitalWrite(9, LOW);
+    bridge1.digitalWrite(9, LOW); // Digital Pin 9 on the second board will be set LOW
+    bridge1.virtualWrite(V5, 2); // Virtual Pin 5 on the second board will be set HIGH
   }
   // Toggle value
   value = !value;


Correct the include path to the one the Spark IDE wants

--- firmware/examples/parse_hex.ino
+++ firmware/examples/parse_hex.ino
@@ -1,10 +1,10 @@
+#include "spark-parse/spark-parse.h"
+
 // This provides a variable, colour, a function to set the variable from a hex
 // string, set_colour, and programs the RGB LED on the spark core to reflect
 // the RRGGBB value last programmed.  The default on reboot is black.

 // BEWARE: British spelling ahead!
-
-#include "spark-parse.h"

 static int colour = 0;


Reorganize global fields

Reorganize global fields

--- examples/quadcopter/quadcopter.ino
+++ examples/quadcopter/quadcopter.ino
@@ -3,49 +3,51 @@
 #include "MINDS-i-Drone.h"
 #include "platforms/Quadcopter.h"

-boolean calibrated = false;
-boolean altHold = false;
-float outputThrottle;
-uint32_t calStartTime;
-const uint8_t CHANNEL_MIN = 32;
-const uint8_t CHANNEL_MAX = 148;
-float yawTarget = 0.0;
-
+// Radio Channel Mapping
+enum RadioChannel{ RADIO_PITCH = 0, RADIO_ROLL = 1, RADIO_THROTTLE = 2,
+                   RADIO_YAW   = 3, RADIO_GEAR = 4, RADIO_AUX      = 5 };
+const uint8_t CHANNEL_TRIGGER_MIN = 32;
+const uint8_t CHANNEL_TRIGGER_MAX = 148;
+
+// State timer used to detect ARMING
+StateTimer radioDownRight([](){
+    bool down  = APMRadio::get(RADIO_THROTTLE) <= CHANNEL_TRIGGER_MIN;
+    bool right = APMRadio::get(RADIO_YAW)      <= CHANNEL_TRIGGER_MIN;
+    return down && right;
+});
+
+// State timer used to detect DISARMING
+StateTimer radioDownLeft([](){
+    bool down  = APMRadio::get(RADIO_THROTTLE) <= CHANNEL_TRIGGER_MIN;
+    bool left  = APMRadio::get(RADIO_YAW)      >= CHANNEL_TRIGGER_MAX;
+    return down && left;
+});
+
+// State machine Variables
 enum State { DISARMED, CALIBRATE, FLYING } state;
 const char* stateString[] = {
     [DISARMED] = "DISARMED", [CALIBRATE] = "CALIBRATE", [FLYING] = "FLYING" };
-enum RadioChannel{ RADIO_PITCH = 0, RADIO_ROLL = 1, RADIO_THROTTLE = 2,
-                   RADIO_YAW   = 3, RADIO_GEAR = 4, RADIO_AUX      = 5 };
-
-StateTimer radioDownRight([](){
-    bool down  = APMRadio::get(RADIO_THROTTLE) <= CHANNEL_MIN;
-    bool right = APMRadio::get(RADIO_YAW)      <= CHANNEL_MIN;
-    return down && right;
-});
-
-StateTimer radioDownLeft([](){
-    bool down  = APMRadio::get(RADIO_THROTTLE) <= CHANNEL_MIN;
-    bool left  = APMRadio::get(RADIO_YAW)      >= CHANNEL_MAX;
-    return down && left;
-});
-
-
-
-//
+
+// Flight State variables
+boolean altHold = false;
+float yawTarget = 0.0;
+uint32_t calStartTime;
+
+// Temporary test variables
 uint32_t loopstart;
 uint32_t loopcount;
-//
-
-void setup() {
-    setupQuad();
-    setState(DISARMED);
-}
+float outputThrottle;

 void setState(State s){
     state = s;
     comms.sendString(stateString[s]);
     loopstart = micros();
     loopcount = 0;
+}
+
+void setup() {
+    setupQuad();
+    setState(DISARMED);
 }

 void loop() {
@@ -97,15 +99,15 @@
 }

 void fly(){
-    const float pitchCmd = ((float)APMRadio::get(RADIO_PITCH)-90) /-70.0;
-    const float rollCmd  = ((float)APMRadio::get(RADIO_ROLL)-90)  /-70.0;
-    const float yawCmd   = ((float)APMRadio::get(RADIO_YAW)-90)   / 90.0;
-    const float throttle = ((float)APMRadio::get(RADIO_THROTTLE)-25)/130.0;
-    const bool  gearCmd  = (APMRadio::get(RADIO_GEAR) > 90);
+    float pitchCmd = ((float)APMRadio::get(RADIO_PITCH)-90) /-70.0;
+    float rollCmd  = ((float)APMRadio::get(RADIO_ROLL)-90)  /-70.0;
+    float yawCmd   = ((float)APMRadio::get(RADIO_YAW)-90)   / 90.0;
+    float throttle = ((float)APMRadio::get(RADIO_THROTTLE)-25)/130.0;
+    bool  gearCmd  = (APMRadio::get(RADIO_GEAR) > 90);
     altitudeCalculation();

     // check for low throttle standby mode
-    if(APMRadio::get(RADIO_THROTTLE) <= CHANNEL_MIN){
+    if(APMRadio::get(RADIO_THROTTLE) <= CHANNEL_TRIGGER_MIN){
         output.standby();
         return;
     } else {


SetTime: swapped in/out, they are reversed on the schematics

--- SetTime/SetTime.ino
+++ SetTime/SetTime.ino
@@ -125,8 +125,8 @@

 /* End of networking code */

-int out = 2;
-int in = 3;
+int out = 3;
+int in = 2;
 int debug = 4;

 /* Bit sequence sending declarations */


Update ds18b20_test.ino

Update ds18b20_test.ino

--- firmware/examples/ds18b20_test.ino
+++ firmware/examples/ds18b20_test.ino
@@ -27,7 +27,6 @@
 if (millis() > DS18B20nextSampleTime){
   getTemp();
   }
-  displayupdate();

   if (millis() > MetricnextPublishTime){
     Serial.println("Publishing now.");


Here's the other path fix...

Here's the other path fix...

Thanks again!
R

--- firmware/examples/voltage_and_current/voltage_and_current.ino
+++ firmware/examples/voltage_and_current/voltage_and_current.ino
@@ -1,6 +1,6 @@
 // EmonLibrary examples openenergymonitor.org, Licence GNU GPL V3

-#include "EmonLib.h"             // Include Emon Library
+#include "EmonLib/EmonLib.h"             // Include Emon Library
 EnergyMonitor emon1;             // Create an instance

 void setup()


added comments for sliderEvent function, problems are a lot i guess

--- glass/glass.ino
+++ glass/glass.ino
@@ -99,9 +99,13 @@
   DEBUG_PRINT("   Softpot Reading: ");
   DEBUG_PRINTLN(softpotReading);

+  // gStart_time != 0  => user has pressed, and the detection has started
+  // (millis() - gStart_time) > gResolution)  => current time has exceeded the gResolution time from when user pressed, I saw example online which set the gResolution be 300ms, though I use 500 to capture double tap
   if (gStart_time != 0 && (millis() - gStart_time) > gResolution) {
     gState = gIDLE;
     gStart_time = 0;
+    // if now user still holds
+    // determine based on click numbers => 0: HOVER; 1:TAP; 2:D_TAP
     if (softpotReading < SOFTPOT_THREASHOLD) {
       if (clicks == 0) {
 	returnV = gHOVER;
@@ -114,6 +118,8 @@
       }
     }
     else
+      // if the user has removed the touch, then probably CLICK event
+      // might also be D_TAP... bug here
       returnV = gCLICK;
     clicks = 0;
     return returnV;
@@ -142,6 +148,7 @@
       *delta = (softpotReading - softpotInitV) / delta_threshold;
     }
     else {
+      // only add to click when state goes from PRESS to RELEASE
       gState = gRELEASE;
       clicks++;
     }


setting time

setting time

--- vibrate_alarm_clock_2.ino
+++ vibrate_alarm_clock_2.ino
@@ -30,7 +30,7 @@
 CLOCK_STATE currentState = NORMAL;

 typedef enum {
-  T_HOUR, T_MINUTE, T_SECOND, T_DAY, T_MONTH, T_YEAR}
+  T_HOUR, T_MINUTE}
 SETTING_TIME_STATE ;
 SETTING_TIME_STATE settingTimeProcess;

@@ -229,27 +229,6 @@
       case T_MINUTE:
         newTime = DateTime(now.year(), now.month(), now.day(), now.hour(), getNextMinSecFromCurrentMinSec(now.minute(), false), now.second());
         break;
-      case T_SECOND:
-        newTime = DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), getNextMinSecFromCurrentMinSec(now.second(), false));
-        break;
-      case T_DAY:
-        newTime = DateTime(now.year(), now.month(), now.day() - 1, now.hour(), now.minute(), now.second());
-        break;
-      case T_MONTH:
-      {
-        int month = now.month();
-        if(month == 1){
-          month = 12;
-        } else {
-          month--;
-        }
-
-        newTime = DateTime(now.year(), month, now.day(), now.hour(), now.minute(), now.second());
-      }
-        break;
-      case T_YEAR:
-        newTime = DateTime(now.year() - 1, now.month(), now.day(), now.hour(), now.minute(), now.second());
-        break;
       default: break;

     }
@@ -306,14 +285,6 @@
     {
       if(settingTimeProcess == T_HOUR){
          settingTimeProcess = T_MINUTE;
-      } else if(settingTimeProcess == T_MINUTE){
-         settingTimeProcess = T_SECOND;
-      } else if(settingTimeProcess == T_SECOND){
-         settingTimeProcess = T_DAY;
-      } else if(settingTimeProcess == T_DAY){
-        settingTimeProcess = T_MONTH;
-      } else if(settingTimeProcess == T_MONTH){
-         settingTimeProcess = T_YEAR;
       } else {
         currentState = NORMAL;
       }
@@ -389,26 +360,6 @@
       case T_MINUTE:
         newTime = DateTime(now.year(), now.month(), now.day(), now.hour(), getNextMinSecFromCurrentMinSec(now.minute(), true), now.second());
         break;
-      case T_SECOND:
-        newTime = DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), getNextMinSecFromCurrentMinSec(now.second(), true));
-        break;
-      case T_DAY:
-        newTime = DateTime(now.year(), now.month(), now.day() + 1, now.hour(), now.minute(), now.second());
-        break;
-      case T_MONTH:
-      {
-        int month = now.month();
-        if(month == 12){
-          month = 1;
-        } else {
-          month++;
-        }
-        newTime = DateTime(now.year(), month, now.day(), now.hour(), now.minute(), now.second());
-      }
-        break;
-      case T_YEAR:
-        newTime = DateTime(now.year() + 1, now.month(), now.day(), now.hour(), now.minute(), now.second());
-        break;
       default: break;

     }


COH-80: WIP

COH-80: WIP

--- COHEAT2015/20160316-COHEAT-M1/COHEAT-M1/COHEAT-M1.ino
+++ COHEAT2015/20160316-COHEAT-M1/COHEAT-M1/COHEAT-M1.ino
@@ -1157,6 +1157,8 @@
           if(!OTV0P2BASE::getPrimaryBuilding16ByteSecretKey(key))
             { OTV0P2BASE::serialPrintlnAndFlush(F("!TX key")); return(false); } // FAIL
           const OTRadioLink::SimpleSecureFrame32or0BodyTXBase::fixed32BTextSize12BNonce16BTagSimpleEnc_ptr_t e = OTAESGCM::fixed32BTextSize12BNonce16BTagSimpleEnc_DEFAULT_STATELESS;
+          // Note: hub must indicate which relay it is sendinf (reverse traffic) to.
+          setTXID(FHT8V.nvGetHC1(), FHT8V.nvGetHC2());
           uint8_t sbuf[OTRadioLink::SecurableFrameHeader::maxSmallFrameSize];
           const uint8_t sbodylen = secureTXState.generateSecureOStyleFrameForTX(sbuf, sizeof(sbuf), OTRadioLink::FTS_RESERVED_Q, lenTXID, txbuf, bodylen, e, NULL, key);
           const bool success = (0 != sbodylen) && PrimaryRadio.sendRaw(sbuf+1, sbodylen-1);


Introduced fan-only relay & filter timing

--- hvacMonitor.ino
+++ hvacMonitor.ino
@@ -19,9 +19,9 @@

 char auth[] = "fromBlynkApp";
 char ssid[] = "ssid";
-char pass[] = "pass";
-
-char myEmail[] = "kemperm@usa.net";
+char pass[] = "pw";
+
+char myEmail[] = "email";

 char* hostSF = "raspi";
 char* streamId   = "publicKey";
@@ -33,7 +33,7 @@
 WidgetTerminal terminal(V26);

 const int blowerPin = 13;  // WeMos pin D7.
-//const int fanPin;          // Future fan only pin.
+const int fanPin = 14;     // Future fan only pin.
 bool daySet = FALSE;       // Sets the day once after reset and RTC is set correctly.

 bool runOnce = TRUE;
@@ -75,6 +75,7 @@

 double tempRA, tempSA;                // Return and supply air temperatures

+// HVAC control pins
 int coolingRelay = 5;   // WeMos pin D1.
 int heatingRelay = 4;   // WeMos pin D2.
 int fanRelay = 16;      // WeMos pin D0.
@@ -122,7 +123,7 @@
   digitalWrite(bypassRelay, HIGH);

   // START OTA ROUTINE
-  ArduinoOTA.setHostname("HVAC-WeMosD1mini");     // Name that is displayed in the Arduino IDE.
+  ArduinoOTA.setHostname("hvacMonitor");     // Name that is displayed in the Arduino IDE.

   ArduinoOTA.onStart([]() {
     Serial.println("Start");
@@ -214,30 +215,30 @@
       }
     case 4: {                         // Manual cooling
         /*
-        controlMode = 0;
-        controllingSpace = "";
-        Blynk.setProperty(V39, "label", "Setpoint");
-        digitalWrite(fanRelay, HIGH);
-        digitalWrite(coolingRelay, LOW);
-        digitalWrite(heatingRelay, HIGH);
-        digitalWrite(bypassRelay, LOW);
+          controlMode = 0;
+          controllingSpace = "";
+          Blynk.setProperty(V39, "label", "Setpoint");
+          digitalWrite(fanRelay, HIGH);
+          digitalWrite(coolingRelay, LOW);
+          digitalWrite(heatingRelay, HIGH);
+          digitalWrite(bypassRelay, LOW);
         */
         controlReset();
-
+
         break;
       }
     case 5: {                         // Manual heating
         /*
-        controlMode = 0;
-        controllingSpace = "";
-        Blynk.setProperty(V39, "label", "Setpoint");
-        digitalWrite(fanRelay, HIGH);
-        digitalWrite(coolingRelay, HIGH);
-        digitalWrite(heatingRelay, LOW);
-        digitalWrite(bypassRelay, LOW);
+          controlMode = 0;
+          controllingSpace = "";
+          Blynk.setProperty(V39, "label", "Setpoint");
+          digitalWrite(fanRelay, HIGH);
+          digitalWrite(coolingRelay, HIGH);
+          digitalWrite(heatingRelay, LOW);
+          digitalWrite(bypassRelay, LOW);
         */
         controlReset();
-
+
         break;
       }
     case 6: {                                         // Keaton-controlled cooling
@@ -342,12 +343,12 @@
   if (controlMode == 1 && digitalRead(blowerPin) == LOW && coolingMode == 0 && millis() >= controlStart + heatControlTimeout) {
     safetyShutoff();

-    Blynk.notify(hour() + String(":") + minute() + ":" + second() + " " + month() + "/" + day() + " ERROR: Heater ran for more than " + (heatControlTimeout/60000) + " minutes.");
+    Blynk.notify(hour() + String(":") + minute() + ":" + second() + " " + month() + "/" + day() + " ERROR: Heater ran for more than " + (heatControlTimeout / 60000) + " minutes.");
   }
   else if (controlMode == 1 && digitalRead(blowerPin) == LOW && coolingMode == 1 && millis() >= controlStart + coolControlTimeout) {
     safetyShutoff();

-    Blynk.notify(hour() + String(":") + minute() + ":" + second() + " " + month() + "/" + day() + " ERROR: Cooler ran for more than " + (coolControlTimeout/60000) + " minutes.");
+    Blynk.notify(hour() + String(":") + minute() + ":" + second() + " " + month() + "/" + day() + " ERROR: Cooler ran for more than " + (coolControlTimeout / 60000) + " minutes.");
   }

 }
@@ -630,38 +631,43 @@

 void sendTemps()
 {
-  sensors.requestTemperatures();                                            // Polls the sensors
+  sensors.requestTemperatures();                                          // Polls the sensors
   tempRA = sensors.getTempF(ds18b20RA);
   tempSA = sensors.getTempF(ds18b20SA);
   tempSplit = tempRA - tempSA;

   // RETURN AIR
-  if (tempRA >= 0 && tempRA <= 150 && digitalRead(blowerPin) == LOW)        // If temp 0-120F and blower running...
-  {
-    Blynk.virtualWrite(0, tempRA);                                          // ...display the temp...
-
-  }
-  else if (tempRA >= 0 && tempRA <= 150 && digitalRead(blowerPin) == HIGH)  // ...unless it's not running, then...
-  {
-    Blynk.virtualWrite(0, "OFF");                                           // ...display OFF, unless...
-  }
-  else
-  {
-    Blynk.virtualWrite(0, "ERR");                                           // ...there's an error, then display ERR.
+  if (digitalRead(blowerPin) == LOW)    // If HVAC blower running...
+  {
+    Blynk.virtualWrite(0, tempRA);      // ...display the temp...
+  }
+  else if (digitalRead(fanPin) == LOW)  // ...if fan-only running, display FAN, unless...
+  {
+    Blynk.virtualWrite(0, "FAN");
+  }
+  else if (tempRA <= 0 || tempRA > 150)
+  {
+    Blynk.virtualWrite(0, "ERR");       // ...there's an error, then display ERR, otherwise...
+  }
+  else{
+    Blynk.virtualWrite(0, "OFF");       // ...display OFF.
   }

   // SUPPLY AIR
-  if (tempSA >= 0 && tempSA <= 150 && digitalRead(blowerPin) == LOW)
+  if (digitalRead(blowerPin) == LOW)
   {
     Blynk.virtualWrite(1, tempSA);
   }
-  else if (tempSA >= 0 && tempSA <= 150 && digitalRead(blowerPin) == HIGH)
-  {
+  else if (digitalRead(fanPin) == LOW)
+  {
+    Blynk.virtualWrite(1, "FAN");
+  }
+  else if (tempSA <= 0 || tempSA > 150)
+  {
+    Blynk.virtualWrite(1, "ERR");
+  }
+  else {
     Blynk.virtualWrite(1, "OFF");
-  }
-  else
-  {
-    Blynk.virtualWrite(1, "ERR");
   }

   if (digitalRead(blowerPin) == LOW) {    // If HVAC is running and...
@@ -859,9 +865,7 @@
     {
       ++todaysAccumRuntimeSec;                          // Counts today's AC runtime in seconds.
       Blynk.virtualWrite(111, todaysAccumRuntimeSec);
-      ++currentFilterSec;                               // Counts how many seconds filter is used based on AC runtime.
-      Blynk.virtualWrite(11, currentFilterSec);
-      currentFilterHours = (currentFilterSec / 3600);   // Converts those seconds to hours for display and other uses.
+
     }
     else if (todaysDate != day())
     {
@@ -875,6 +879,12 @@
       todaysDate = day();
     }

+    if (digitalRead(blowerPin) == LOW || digitalRead(fanPin) == LOW) {
+      ++currentFilterSec;                               // Counts how many seconds filter is used for HVAC or fan-only.
+      Blynk.virtualWrite(11, currentFilterSec);
+      currentFilterHours = (currentFilterSec / 3600);   // Converts those seconds to hours for display and other uses.
+    }
+
     if (yesterdayRuntime < 1)               // Displays yesterday's runtime in app, or 'None' is there's none.
     {
       Blynk.virtualWrite(14, "None");


debug

debug

--- jenny5-firmware/jenny5-firmware.ino
+++ jenny5-firmware/jenny5-firmware.ino
@@ -62,13 +62,15 @@
 void setup()
 {
 	first_start = 0;
-	strcpy(firmware_version, "2019.04.26.2");
+	strcpy(firmware_version, "2019.04.28.4");

 	current_buffer[0] = 0;

+
 	tera_ranger_one_lidar = NULL;

 	Serial.begin(115200); //Open Serial connection
+	//Serial.begin(57600); //Open Serial connection

 	//delay(2000);

@@ -223,6 +225,7 @@
 								int num_consumed;
 								sscanf(tmp_str + i + 2, "%d%n", &motor_index, &num_consumed);
 								steppers_controller.stop(motor_index);
+								sprintf(tmp_serial_out, "ST%d#", motor_index);
 								i += 4;
 							}
 							else
@@ -900,6 +903,7 @@
 void loop()
 {
 	char serial_out[MAX_BUFFER_LENGTH];
+	serial_out[0] = 0;

 	// check to see if there are new results from ultrasonic sensor
 	ultrasonic_sensors_controller.update_results(serial_out);
@@ -927,6 +931,15 @@
 				if (strlen(serial_buffer) + strlen(current_buffer) < MAX_BUFFER_LENGTH) // concat only if it fits
 					strcat(current_buffer, serial_buffer);

+			if (current_buffer[0]) {
+				Serial.write("I ");
+				Serial.write(current_buffer);
+				Serial.write("/");
+				Serial.write(serial_buffer);
+
+				Serial.write("#");
+			}
+
 #ifdef DEBUG
 			Serial.write("initial buffer is=");
 			Serial.write(current_buffer);
@@ -935,6 +948,7 @@

 			// parse from the beginning until I find a M, D, L, S, A, P, B, U, G, T
 			int current_buffer_length = strlen(current_buffer);
+//			bool command_found = false;
 			for (int i = 0; i < current_buffer_length; i++)
 				if ((current_buffer[i] >= 'A' && current_buffer[i] <= 'Z') || (current_buffer[i] >= 'a' && current_buffer[i] <= 'z')) { // a command
 				  // find the terminal character #
@@ -950,7 +964,7 @@
 						Serial.write(tmp_str);
 						Serial.println();
 #endif
-
+				//		command_found = true;
 						parse_and_execute_commands(current_buffer + i, j - i, serial_out);
 						if (serial_out[0])
 							Serial.write(serial_out);
@@ -972,6 +986,8 @@
 						break; // for i
 					}
 				}
+			//if (strlen(current_buffer) && !command_found)// wrong string
+				//current_buffer[0] = 0;
 		}
 	}
 	steppers_controller.run_motors(&as5147s_controller,


[avr] TXID-type packets are identified by the low 2 bits being b00

Thanks to wolfer649 for finding this

--- arduino/Powermon433/Powermon433.ino
+++ arduino/Powermon433/Powermon433.ino
@@ -178,6 +178,7 @@
 // Inter-ID-packet delay in msec
 #define OOK_ID_DELAY 225

+#define OOK_PACKET_TXID    0
 #define OOK_PACKET_INSTANT 1
 #define OOK_PACKET_TEMP    2
 #define OOK_PACKET_TOTAL   3
@@ -562,7 +563,9 @@
   dumpRxData();

   uint16_t val16 = decoder.data.val16[0];
-  if (g_DiscoverPeriod && crc8(decoder.data.raw, 3) == 0)
+  if (g_DiscoverPeriod &&
+    (decoder.data.raw[0] & 3) == OOK_PACKET_TXID &&
+    crc8(decoder.data.raw, 3) == 0)
   {
     g_TxId = decoder.data.raw[1] << 8 | decoder.data.raw[0];
     Serial.print(F("NEW DEVICE id="));


Fix buffer overflows.

Fix buffer overflows.

--- Stecchino_Arduino_sketch/Stecchino_Arduino_sketch.ino
+++ Stecchino_Arduino_sketch/Stecchino_Arduino_sketch.ino
@@ -113,7 +113,7 @@

 void LEDS_ON(int count, int record){
-    for (int i = NUM_LEDS; i >=0; i--) {
+    for (int i = NUM_LEDS-1; i >=0; i--) {
       if (i<=NUM_LEDS-count){leds[i]=CRGB::Black;}
       //else {leds[i]=CRGB::Green;}
       else {leds[i]=CHSV(gHue, 255, 255);}
@@ -132,7 +132,7 @@
   if (pattern=="going_to_sleep"){
     digitalWrite(MOSFET_GATE,HIGH);
     FastLED.setBrightness(LOW_BRIGHTNESS);
-    for (int i = NUM_LEDS; i >=0; i--){
+    for (int i = NUM_LEDS-1; i >=0; i--){
       leds[i]=CRGB::Blue;
     }
   }
@@ -148,7 +148,7 @@
   if (pattern=="start_play"){
     digitalWrite(MOSFET_GATE,HIGH);
     FastLED.setBrightness(LOW_BRIGHTNESS);
-    for (int i = NUM_LEDS; i >=0; i--){
+    for (int i = NUM_LEDS-1; i >=0; i--){
       leds[i]=CRGB::Green;
     }
   }
@@ -168,13 +168,13 @@
   if (pattern=="game_over"){
     digitalWrite(MOSFET_GATE,HIGH);
     FastLED.setBrightness(LOW_BRIGHTNESS);
-    for (int i = NUM_LEDS; i >=0; i--){
+    for (int i = NUM_LEDS-1; i >=0; i--){
       leds[i]=CRGB::Red;
     }
   }

   if (pattern=="off"){
-    for (int i = NUM_LEDS; i >=0; i--) {
+    for (int i = NUM_LEDS-1; i >=0; i--) {
       //leds[i]=CRGB::Black;
       leds[i].nscale8(230);
     }
@@ -197,7 +197,7 @@
     //int pos_led=map(int_angle,45,-45,1,NUM_LEDS);
     int pos_led=map(int_angle,-45,45,1,NUM_LEDS);
     int couleur_led=map(pos_led,0,NUM_LEDS,0,255);
-    for (int i = NUM_LEDS; i >=0; i--){
+    for (int i = NUM_LEDS-1; i >=0; i--){
       if (i==pos_led){leds[i]=CHSV(couleur_led, 255, 255);}
       //if (i==pos_led){leds[i]=CRGB::Blue;}
       else {leds[i]=CRGB::Black;}
@@ -222,7 +222,7 @@
     //Serial.println(pos_led);

-    for (int i = NUM_LEDS; i >=0; i--){
+    for (int i = NUM_LEDS-1; i >=0; i--){
       if (i<=pos_led){
         if (i<=5){leds[i]=CRGB::Red;}
         else if (i>5 && i<=15){leds[i]=CRGB::Orange;}
@@ -297,7 +297,7 @@
 }

 void alloff() {
-  for (int i = NUM_LEDS; i >=0; i--) {
+  for (int i = NUM_LEDS-1; i >=0; i--) {
     leds[i]=CRGB::Black;
     delay(10);
     FastLED.show();


Updated MRLComm so that it compilies on other platforms than the ESP8266

--- src/resource/Arduino/MRLComm/MRLComm.ino
+++ src/resource/Arduino/MRLComm/MRLComm.ino
@@ -107,7 +107,9 @@
 	// send back load time and memory
         // driven by getBoardInfo now !!!
         // mrlComm.publishBoardStatus();
-  webSocket.loop();
+  #if defined(ESP8266)
+    webSocket.loop();
+  #endif
 } // end of big loop


Bordem behaviour

Bordem behaviour

--- software/firmware/LogoBotIRSeeker/LogoBotIRSeeker.ino
+++ software/firmware/LogoBotIRSeeker/LogoBotIRSeeker.ino
@@ -6,6 +6,7 @@
 enum Activity
 {
   Idle,
+  Bored,
   Seek,
   Turn,
   Advance,
@@ -19,6 +20,9 @@
 static int scanDir = 1; // direction we are panning the servo in - 1 or -1.
 static long lastScanMove = 0; // last time we moved the servo
 static Activity action = Idle; // current activity the bot is doing
+
+#define BORDEM_THRESHOLD 2 // how long before the bot gets bored and makes a random move
+static int blankScanCount = 0; // number of scans with no interaction

 Bot bot(MOTOR_L_PIN_1, MOTOR_L_PIN_2, MOTOR_L_PIN_3, MOTOR_L_PIN_4, MOTOR_R_PIN_1, MOTOR_R_PIN_2, MOTOR_R_PIN_3, MOTOR_R_PIN_4);
 Servo lidServo;
@@ -53,6 +57,8 @@

       if (dist < DIST_CLOSE) {
         // object close, turn to face or drive towards if already facing
+        blankScanCount = 0;
+
         if (lookAng < 90) {
           action = Turn;
           bot.turn(-1);
@@ -86,8 +92,13 @@
         lookAng += scanDir;

         // switch scan direction if end of servo travel reached
-        if (lookAng > 180) scanDir = -1;
-        if (lookAng < 0) scanDir = 1;
+        if (lookAng > 180) {
+          scanDir = -1;
+        }
+        if (lookAng < 0) {
+          scanDir = 1;
+          blankScanCount++;
+        }
       }

       lastScanMove = millis() + SCAN_SPEED;
@@ -99,6 +110,7 @@
     // Update LED
     switch(action) {
       case Idle:
+      case Bored:
         setLEDColour(0, 0, 0);
         break;
       case Seek:
@@ -111,6 +123,18 @@
       case Retreat:
         setLEDColour(1, 0, 0);
         break;
+    }
+
+    // Bored, nothing much to see here, wander around a bit in the hope of finding something
+    if (action == Seek && blankScanCount > BORDEM_THRESHOLD) {
+      action = Bored;
+      lookAng = 90;
+      if (random(0, 100) > 35)
+        bot.driveTo(random(-50, 50), random(-50, 50));
+      else
+        bot.arcTo(random(-50, 50), random(-50, 50));
+      blankScanCount = 0;
+      lastScanMove = millis() + 5000;
     }
 }


Flip the oled

Flip the oled

--- transmitter/Monitor/Monitor.ino
+++ transmitter/Monitor/Monitor.ino
@@ -34,7 +34,7 @@
 void setup()
 {
   Serial.begin(115200);
-
+  u8g.setRot180();
   memset(&monitor, 0, sizeof(monitor_t));
   memset(&ack_payload, 0, sizeof(ack_t));


Made a much simpler example of connecting to a server and showing progress via the multiColored LED...uses phil's library

--- arduino/arduinoMultiColorLights/arduinoMultiColorLights.ino
+++ arduino/arduinoMultiColorLights/arduinoMultiColorLights.ino
@@ -1,3 +1,5 @@
+#include <MultiLight.h>
+

 #include <SPI.h>
 #include <Ethernet.h>
@@ -5,87 +7,61 @@
 #include <PusherClient.h>

 IPAddress server(50,57,138,30); // ARIS Server
-String socket_id = "socket_id";
 byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x49, 0xDE };
-byte ip[] = { 192,168,2,7 };
-byte gateway[] = { 192,168,2,1 };
+byte ip[] = { 192, 168, 2, 2 };
+byte gateway[] = { 192, 168, 2, 1 };
 byte subnet[] = { 255, 255, 255, 0 };

-int inputPin = 3;
-int ledPin = 2;
+MultiLight multiLight = MultiLight();

-const int RED_LED_PIN = 9;
-const int GREEN_LED_PIN = 10;
-const int BLUE_LED_PIN = 11;
-
-int redIntensity = 0;
-int greenIntensity = 0;
-int blueIntensity = 0;
-
-const int DISPLAY_TIME = 100; //millis
 EthernetClient arisClient;
-PusherClient client;

 void setup() {
-  pinMode(inputPin, INPUT);
-    pinMode(ledPin, OUTPUT);
-Serial.begin(9600);
-
+
+  Serial.begin(9600);
+
+  multiLight.setPins(5,6,7);  //Set up pins

   ethernetConnecting();
-
-    delay(3000);
-
-  ethernetSucceeded();

-      delay(3000);
-
-      ethernetFailed();
-
-
+  Ethernet.begin(mac,ip);
+
+  delay(1000); //Give time to initialize before connecting
+
+  if(arisClient.connect(server,80)){
+     ethernetSucceeded();
+    }
+  else{
+     ethernetFailed();
+    }

 }

 void loop() {
-
- if(digitalRead(inputPin) == HIGH){
-  digitalWrite(ledPin, LOW);
- }
- else{
-   digitalWrite(ledPin, HIGH);
-  // client.triggerEvent("aris","client-test", "{\"name\": \"Joe\", \"message_count\": 23}");
-   Serial.println("Button Pressed");
- }

 }
+
 void ethernetConnecting(){
   Serial.println("Ethernet Connecting");

-
-    analogWrite(GREEN_LED_PIN, 18);
-    analogWrite(BLUE_LED_PIN, 0);
-    analogWrite(RED_LED_PIN, 40);
-
+  multiLight.setColor(multiLight.COLOR_YELLOW);

 }
+
 void ethernetFailed(){
   Serial.println("Ethernet Failed");

-
-    analogWrite(GREEN_LED_PIN, 0);
-    analogWrite(BLUE_LED_PIN, 0);
-    analogWrite(RED_LED_PIN, 20);
+   multiLight.setColor(multiLight.COLOR_RED);
+

 }

 void ethernetSucceeded(){
   Serial.println("Ethernet Succeeded");

-      analogWrite(GREEN_LED_PIN, 20);
-    analogWrite(BLUE_LED_PIN, 0);
-    analogWrite(RED_LED_PIN, 0);
+  multiLight.setColor(multiLight.COLOR_GREEN);
+

-
 }


fixed pins pseudo array error

fixed pins pseudo array error

--- tiltswitch/tiltswitch.ino
+++ tiltswitch/tiltswitch.ino
@@ -6,7 +6,6 @@
 int ledpin = 13; // for output
 int tiltpin = 2; // tiltswitch in put
 int is_on = 0;// boolean
-int [] pins = [13, 12, 11, 10];

 void setup(){
 pinMode(ledpin, OUTPUT); // init


add space

add space

--- invPendulum/invPendulum.ino
+++ invPendulum/invPendulum.ino
@@ -60,7 +60,7 @@
      pos++;
      if (command1.substring(i,i+1) == " "){
         string1 = command1.substring(pos-i,i-1);
-
+
      }

     }


MInor refactoring beacon_gps_update.ino

MInor refactoring beacon_gps_update.ino

--- ESP32/beacon_gps_update/beacon_gps_update.ino
+++ ESP32/beacon_gps_update/beacon_gps_update.ino
@@ -192,7 +192,7 @@
 }

 static bool
-initialize_ble_gap(const char * name){
+bluetooth_GAP_setup(const char * name){
     if(!btStarted() && !btStart()){
         log_e("btStart failed");
         return false;
@@ -244,24 +244,31 @@
     Serial.println(ESP.getSdkVersion());
     //esp_log_level_set("*", ESP_LOG_VERBOSE);
     gps_setup();
-    initialize_ble_gap("C9999");
+    bluetooth_GAP_setup("C9999"); //The node name
  }

+
+void
+watchdog_into( beacon_data_t * beaconData )
+{
+    static long counter = 0;
+    counter = counter + 1;
+    beaconData->fields.watchdogCounter = counter;
+}

 void
 loop() {
+    // Fill the advertisement packet with GPS data
     gps_read_into( &myBeaconData );
-
-    if(esp_ble_gap_config_adv_data(&advertisement_config))
+    watchdog_into( &myBeaconData );
+
+    //The advertisement fires regularly after setup, just update the data it sends
+    //advertisement_config.p_manufacturer_data field points to myBeaconData
+    if(esp_ble_gap_config_adv_data(&advertisement_config))
     {   log_e("gap_config_adv_data failed");
         exit(1);
     }
+
     delay(500); digitalWrite(LED_BUILTIN, HIGH);
     delay(500); digitalWrite(LED_BUILTIN, LOW);
-
-          //delay(1000);
-
-    //static long counter = 0;  //example only
-    //counter = counter + 1;    //example only
-    //myBeaconData.fields.watchdogCounter              = counter;
-}
+}


Receive may freeze if CAN saturated

Receive may freeze if CAN saturated

If either the CANBUS is saturated or the MCU is busy for some time, both RX buffers of the MCP2515 may be in use. If the MCU does not catch up in processing the incoming messages, the second message received just adds another reason for the IRQ.
Thus reading a single message does not clear the IRQ conditon of the MCP2515 leading to a permanent lock up of the receiver program.

Signed-off-by: Ralf Edmund Stranzenbach <92f48d309cda194c8eda36aa8f9ae28c488fa208@reswi.de>

--- examples/receive_interrupt/receive_interrupt.ino
+++ examples/receive_interrupt/receive_interrupt.ino
@@ -41,20 +41,27 @@

 void loop()
 {
-    if(Flag_Recv)                   // check if get data
-    {
+  if(Flag_Recv) {                   // check if get data

-        Flag_Recv = 0;                // clear flag
-        CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf
-
-        for(int i = 0; i<len; i++)    // print the data
-        {
-            Serial.print(buf[i]);Serial.print("\t");
-        }
-        Serial.println();
-    }
+    Flag_Recv = 0;                // clear flag
+
+    // iterate over all pending messages
+    // If either the bus is saturated or the MCU is busy,
+    // both RX buffers may be in use and reading a single
+    // message does not clear the IRQ conditon.
+    while (CAN_MSGAVAIL == CAN.checkReceive()) {
+      // read data,  len: data length, buf: data buf
+      CAN.readMsgBuf(&len, buf);
+
+      // print the data
+      for(int i = 0; i<len; i++) {
+	Serial.print(buf[i]);Serial.print("\t");
+      }
+      Serial.println();
+    }
+  }
 }

 /*********************************************************************************************************
   END FILE
-*********************************************************************************************************/+*********************************************************************************************************/


changed to warm set point

changed to warm set point

--- chillduino.ino
+++ chillduino.ino
@@ -44,12 +44,12 @@
 #define LED_ON_G         255
 #define LED_ON_B         255

-#define THERMISTOR_MIN_COLD    294
-#define THERMISTOR_MAX_COLD    392
-#define THERMISTOR_MIN_COLDER  294
-#define THERMISTOR_MAX_COLDER  392
-#define THERMISTOR_MIN_COLDEST 294
-#define THERMISTOR_MAX_COLDEST 392
+#define THERMISTOR_MIN_COLD    231
+#define THERMISTOR_MAX_COLD    335
+#define THERMISTOR_MIN_COLDER  231
+#define THERMISTOR_MAX_COLDER  335
+#define THERMISTOR_MIN_COLDEST 231
+#define THERMISTOR_MAX_COLDEST 335

 #define TICKS_PER_SECOND   ((unsigned long) 1000)
 #define TICKS_PER_MINUTE   (60 * TICKS_PER_SECOND)


Added header comment for bar graph example

Signed-off-by: Irmo van den Berge <0370dce6b07866a7132582d46388cf81833cb900@home.nl>

--- examples/AnalogBarGraph/AnalogBarGraph.ino
+++ examples/AnalogBarGraph/AnalogBarGraph.ino
@@ -1,3 +1,6 @@
+/*
+ * Displays the raw analog input values in 6 bar graph widgets
+ */
 #include "Phoenard.h"

 // Define six bar graphs