Lots of changes. Seems to be working

Lots of changes. Seems to be working

--- analogjson.ino
+++ analogjson.ino
@@ -0,0 +1,1042 @@
+//
+// Copyright (c) 2015 Neil Webber.
+//
+// See LICENSE file for licensing terms.
+//
+
+#include <SPI.h>
+#include <Ethernet.h>
+
+#include "jsmn.h"     // JSON parser
+
+// A NOTE ABOUT STRINGS
+//
+// There are many string literals in this example. They use up RAM.  They
+// could be moved into program memory and accessed via PROGMEM technique;
+// however, that makes the code a lot rougher on the eyes. This all
+// currently still fits as is so I have left the strings alone.
+//
+
+
+// ---------------------
+// Arbitrary Size Limits
+// ---------------------
+//
+// Tradeoff between simplicity (e.g., not using malloc) and flexibility
+// You need to set the REQUESTBUFFERSIZE to be large enough for the larger of:
+//      -- the biggest URL you are going to use
+//      -- the biggest JSON data you are going to exchange
+//
+// Similarly, you need to set the MAXJSMNTOKENS to be large enough so that
+// the JSMN parser can parse whatever JSON you send.
+//
+// In both cases if something comes in that is too large it will just be
+// discarded and an error returned to the client.
+//
+// You have control over all of this based on your application.
+// If all you are doing is getting/setting one or two pins at a time
+// you won't need much space. If you are trying to allow for lots of pins
+// in a single HTTP transaction you will need more space, and of course
+// at some point the Arduino RAM limitations come into play.
+//
+// These default constants allow most requests to have a few pins specified.
+
+#ifndef REQUESTBUFFERSIZE
+#define REQUESTBUFFERSIZE 200
+#endif
+
+#ifndef MAXJSMNTOKENS
+#define MAXJSMNTOKENS 30
+#endif
+
+// this is the maximum number of pins permitted in
+// various multi-pin things. It's loosely related to tokens
+// though in fact the exact relationship isn't this simple
+
+#define MAXPINS (MAXJSMNTOKENS/2)  // maximum # of pins in a single request
+
+
+// The MAC address this Arduino will use
+//
+// Arduino Ethernet shields come with an address; mine were labeled
+// on the down side of the board.  Use that (edit it in here) if you can.
+//
+// Otherwise, make one up like this. If you are making it up then the first
+// byte MUST be even and it SHOULD NOT be a multiple of 4 (official ones
+// are multiples of 4).  See, e.g., "MAC_address" on wikipedia.
+//
+byte mac[] = { 0x66, 0x66, 0x65, 0x11, 0x22, 0x34 };
+
+// web server on port 80
+EthernetServer server(80);
+
+// version string
+const char revision[] = "20150826.1";
+
+// this counter is for statistics; reported via status API
+unsigned long requests_processed = 0;
+
+
+// compute elapsed time (in milliseconds) from time t0
+// Takes millis() wrap-around into account, though obviously
+// 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 t1 = millis();
+    if (t1 >= t0)
+        return t1 - t0;
+    else {
+        //
+        // t1 is "before" t0, which presumably means the timer has
+        // wrapped around (happens after about 50 days of up time).
+        //
+        // I assume there is some simpler/more-clever way to do this, but:
+        // Basically we need to add 0x100000000 to the incorrectly "lower"
+        // time t1 before doing the subtraction, but of course we need
+        // to do this carefully to stay within 32 bits to make it work.
+        //
+
+        // we want (t1 + 0x100000000) - t0 and get there this way:
+        //     0xFFFFFFFF - t0
+        //        +t1             (will not overflow because we know t1<t0)
+        //        +1
+
+        unsigned long tmp = 0xFFFFFFFF;
+        tmp -= t0;     // 0xFFFFFFFF - t0
+        tmp += t1;     // (0xFFFFFFFF - t0) + t1
+
+        tmp &= 0xFFFFFFFF;  // make sure it works on 64 bit machines (haha)
+        return tmp + 1; // the rest of the 0x100000000 we needed to add
+    }
+}
+
+// ---------------
+//  JSMN helpers
+// ---------------
+//
+// The JSMN "parser" is really just a JSON tokenizer.
+// These routines help with (specialized/targeted) parsing of JSON trees
+// from the tokenized output of JSMN.
+//
+
+// String compare:
+//   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)
+{
+    return strncmp(k, &buf[tp->start], tp->end - tp->start);
+}
+
+// Integer conversion:
+//   tp is a jsmn token pointing into (start/stop indices into) buf
+//   Convert it to an integer, 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)
+
+int
+jsmn_atoi(const jsmntok_t *tp, const char *buf)
+{
+    int rv = 0;
+    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;
+}
+
+
+// Token skip:
+//
+// You've got a token list that starts with a token you aren't interested in.
+// This skips forward over that token and everything it recursively contains.
+//
+// Returns the number of tokens skipped, which by definition is always
+// at least one (the one you initially point us at)
+//
+// It's up to you to know whether this was your last token (i.e., if
+// by skipping it you've reached the end of the token list)
+//
+
+int
+jsmn_skiptok(const jsmntok_t *toks)
+{
+    int nskipped = 1;   // i.e. we already know we're skipping THIS one
+    int n;
+
+    // for JSMN_PRIMITIVE or JSMN_STRING, by definition we're only
+    // skipping one token, so there's nothing else to do.
+    //
+    // For the ARRAY or OBJECT containers, iterate through their contents
+    // and recursively skip them.
+
+    if (toks->type == JSMN_ARRAY) {
+	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--)
+            nskipped += 1 + jsmn_skiptok(toks+nskipped+1); // key and value
+    }
+
+    return nskipped;
+}
+
+// Extract a key value from an object
+//
+//    objtp points to the start of a JSMN_OBJECT in a token list
+//    buf is the character buffer for the tokens
+//    k is the key you seek
+//
+// 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 nskipped = 1;
+
+    if (objtp->type == JSMN_OBJECT) {
+	for (int n = objtp->size; n > 0; n--) {
+            jsmntok_t *tp = objtp+nskipped;
+
+            if (jsmn_strcmp(tp, k, buf) == 0)
+		return nskipped+1;
+            else
+                nskipped += 1 + jsmn_skiptok(tp+1);   // skip key and value
+	}
+    }
+    return -1;
+}
+
+// -----------------------
+// HTTP/Ethernet utilities
+// -----------------------
+
+
+// eatline - consume characters until LF
+//
+// Strictly speaking we should be looking for CRLF but
+// this works for all properly formatted requests
+//
+// Returns -1 if there was a problem (timeout, connection lost, etc)
+//
+
+int
+eatline(EthernetClient &ec, unsigned long t0)
+{
+    for(;;) {
+        int i;
+
+        if ((i = clientgetc(ec, t0)) == -1)
+            return -1;
+        if (i == '\n')
+            return 0;
+     }
+}
+
+// skipwhite - consume white space characters
+//
+// Returns the first non-space character or -1 for timeouts/disconnect/etc
+
+int
+skipwhite(EthernetClient &ec, unsigned long t0)
+{
+    for(;;) {
+        int i;
+
+        if ((i = clientgetc(ec, t0)) == -1)
+            return -1;
+        if (! isspace((char)i))
+            return i;
+     }
+}
+
+// get a character
+//
+// Get a character from the client, with timeout and disconnect checks.
+// Returns the character, or -1 if unsuccessful.
+//
+// To protect against clients that connect and send nothing this bails
+// out after "too long". No "real" browser ever causes this but you can
+// test it by telnetting to port 80, entering at least one character and
+// then just waiting.
+//
+
+#define HTTPPARSE_TIMEOUT_SECONDS    20L
+#define HTTPPARSE_TIMEOUT            (HTTPPARSE_TIMEOUT_SECONDS * 1000L)
+
+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
+    //      have anything else to do and the TIMEOUT eventually applies.
+
+    while ((elapsed_time(t0) <= HTTPPARSE_TIMEOUT) && ec.connected()) {
+        if (ec.available())
+            return ec.read();
+    }
+    return -1;
+}
+
+// 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 = millis();
+
+    for ( ; n > 0 ; --n) {
+        int i = clientgetc(ec, t0);
+
+        if (i == -1)
+            return -1;
+
+        *buf++ = (char)i;
+    }
+    *buf = '\0';
+    return 0;
+}
+
+// ------------
+// HTTP Parsing
+// ------------
+//
+// First a little request structure to hold results and context:
+//     client         -- the EthernetClient we are working with
+//     bufp           -- buffer to hold the resulting URL
+//     bufsiz         -- how big bufp is
+//     content_length -- if a Content-Length: header was given, the
+//                       value supplied; else -1.
+//     operation      -- HTTPPARSE_GET or HTTPPARSE_POST
+
+#define HTTPPARSE_NONE               0
+#define HTTPPARSE_GET                1
+#define HTTPPARSE_POST               2
+
+struct http_rq {
+    EthernetClient &client;
+    unsigned long t0;         // used for enforcing timeouts
+    int  content_length;
+    int  operation;
+    char *bufp;
+    int bufsiz;
+    http_rq(EthernetClient &ec) : client(ec) {}  // reference requires init
+};
+
+// ------------------
+// URL function table
+// ------------------
+//
+// This is instantiated later (after all the function definitions)
+// This table connects particular URLs, operations, and their implementation
+//
+
+struct urlfuncs {
+    char *url;
+    int whatop;
+    void (* f)(struct http_rq *, struct urlfuncs *);
+};
+
+
+
+
+
+
+// Parsing the first line of an HTTP request
+// By RFC, it should look like:
+//
+//       [VERB][SP][URL][SP]HTTP/1.1[CRLF]
+//
+// where:
+//    [VERB] is "GET" or "POST" etc and is CASE SENSITIVE (per RFC)
+//    [SP]   is literally and EXACTLY ONE space character (per RFC)
+//    [URL]  is the /blah/foo/frammis part
+//    [CRLF] is literally/exactly '\r\n'
+//
+// The RFC is very specific and strict on this format, and we are not
+// very forgiving here in the interests of keeping a small code footprint.
+//
+// Returns zero for success.
+//
+
+int
+parsefirstline(struct http_rq *rp)
+{
+    char *p = NULL;
+    char c;
+    int i;
+
+    // get the very first character, which since we only understand
+    // GET and POST is enough to distinguish those cases. If we ever
+    // add PUT requests this will need some minor rework.
+
+    if ((i = clientgetc(rp->client, rp->t0)) == -1)
+        return -1;
+    c = (char)i;
+    if (c == 'G') {
+        rp->operation = HTTPPARSE_GET;
+        p = "ET";
+    } else if (c == 'P') {
+        // if PUT becomes an option, add more logic here; for now...
+        rp->operation = HTTPPARSE_POST;
+        p = "OST";
+    } else
+        return -1;
+
+    // at this point we are looking for the rest of GET/POST (or disconnected)
+    while (*p != '\0') {
+        if ((i = clientgetc(rp->client, rp->t0)) == -1)
+            return -1;
+        c = (char)i;
+        if (c != *p++)        // i.e., not matching expected GET or POST
+            return -1;
+    }
+
+    // next character must be a space per RFC
+    if (clientgetc(rp->client, rp->t0) != ' ')
+        return -1;
+
+    // at this point we matched the verb and have read the space. Yay!
+    // What comes next should be the url; put it into the buffer.
+    // NOTE: It's up to you to define your protocol endpoints to fit in
+    // the buffer size you also defined. We check for overflow; overflow
+    // causes the request to be discarded. Choose your compile-time
+    // constants/sizes according to your application requirements.
+
+    p = rp->bufp;
+
+    for(;;) {   // go until hit ' ' or timeout or buffer limit
+        if ((i = clientgetc(rp->client, rp->t0)) == -1)
+            return -1;
+        c = (char)i;
+        if (c == ' ') {          // end of the URL
+            *p = '\0';           // note we ensured room for this (bufsiz-1)
+            break;
+        }
+        if (p == rp->bufp+(rp->bufsiz-1))   // always leave room for '\0'
+            return -1;            // hit end of buffer before end of URL
+        *p++ = c;                 // still have room; copy this char
+    }
+
+    // have the URL so now consume the HTTP/1.1 part. We don't check it.
+    return eatline(rp->client, rp->t0);
+}
+
+// Parsing Content-Length
+//
+// This is called whenever a header field starts with 'C', and if
+// the header field does in fact match Content-Length: then we
+// parse it and store the length value.
+//
+// Note that header fields are cAse iNSensiTiVe
+//
+// All other headers starting with C are ignored (as are all other headers)
+//
+int
+tryCLen(struct http_rq *rp)
+{
+    const char *p = "ONTENT-LENGTH:";
+    int i;
+    char c;
+
+    for(;;) {
+        if ((i = clientgetc(rp->client, rp->t0)) == -1)
+            return -1;
+        c = (char)i;
+
+        if (c == ':' && *p == ':')
+            break;                         // woohoo, found it!
+
+        if (isalpha(c))
+            c = toupper(c);
+
+        if (c != *p++)                   // nope, not content-length
+            return eatline(rp->client, rp->t0);
+    }
+
+    // we have Content-Length: ... now there is arbitrary white space
+    if ((i = skipwhite(rp->client, rp->t0)) == -1)
+        return -1;
+    c = (char)i;
+
+    // convert the content-length field, note we already have the first char
+    rp->content_length = (c - '0');
+    for (;;) {
+        if ((i = clientgetc(rp->client, rp->t0)) == -1)
+            return -1;
+        c = (char)i;
+        if (isdigit(c)) {
+            rp->content_length *= 10;
+            rp->content_length += (c - '0');
+        } else if (c == '\n')         // no CR? really shouldn't happen
+            return 0;
+        else
+            return eatline(rp->client, rp->t0);
+    }
+}
+
+//
+// The top level for all this parsing mess.
+// Returns 0 for success, -1 for error.
+//
+// Not fully RFC-compliant parsing; we have compromised for footprint.
+// Does not enforce the requirement for a Host: header (which we'd ignore
+// so we don't even look for it; this permissiveness violates the RFC).
+//
+// On return, the Ethernet client stream will have been read through the
+// blank line (\r\n) terminating the headers (i.e., the next byte should be
+// the first byte of the POST data if there is any)
+//
+// 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 i;
+    char c;
+
+    rp->operation = HTTPPARSE_NONE;
+    rp->content_length = -1;
+
+    if (parsefirstline(rp) == -1)
+        return -1;
+
+    // so, the deal is we need to consume all the headers.
+    // we are only checking for one very specific header: Content-Length.
+    // If that is encountered we'll fill in the rp->content_length field.
+    // Everything else is just consumed/ignored.
+
+    int linelen = 0;    // will track as we go; a zero length line ends headers
+    for(;;) {
+        if ((i = clientgetc(rp->client, rp->t0)) == -1)
+            return -1;
+        c = (char)i;
+        if (isalpha(c))
+            c = toupper(c);
+
+        if (c == '\n') {
+            if (linelen == 0)
+                return 0;         // got a blank line
+            linelen = 0;
+        } else if (c != '\r') {   // not conformant, but sufficient: ignore CR
+            ++linelen;
+            if (c == 'C' && linelen == 1) {
+                // headers starting with 'C' are further inspected...
+                if (tryCLen(rp) == -1)
+                    return -1;
+                linelen = 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";
+
+// basic unsuccessful reply
+const char reply400[] =
+    "HTTP/1.1 400 Bad Request\r\n\Connection: close\r\n\r\n";
+
+
+// Guts of actually reading pins and producing JSON results
+void
+processjsonreads(EthernetClient &ec,
+                 jsmntok_t *tp,
+                 char *bufp,
+                 int (*readerfunc)(uint8_t))
+{
+    int pins_to_read[MAXPINS];
+    int npins = 0;
+    int n;
+
+    if (tp->type == JSMN_STRING || tp->type == JSMN_PRIMITIVE) // just "3" etc
+        n = 1;
+    else if (tp->type == JSMN_ARRAY) {  // [ 1, 3, { "something bogus" : 1 } ]
+        n = tp->size;
+        tp++;    // skip to first contained object
+    }
+
+    for ( ; n > 0; n--) {
+        if (tp->type == JSMN_STRING || tp->type == JSMN_PRIMITIVE) {
+            // ok to be destructive now
+            bufp[tp->end] = '\0';
+            pins_to_read[npins++] = atoi(&bufp[tp->start]);
+        }
+        tp += jsmn_skiptok(tp);
+    }
+    ec.print(httpreply1);
+    ec.print("{\"serverVersion\":\"");
+    ec.print(revision);
+    ec.print("\",\"pins\":[");
+    for (int i = 0; i < npins; i++) {
+        if (i > 0)
+            ec.print(",");
+        ec.print("{\"pin\":");
+        ec.print(pins_to_read[i]);
+        ec.print(",\"value\":");
+        // We certainly could try to enforce some sanity on pinnum,
+        // but instead "you get whatever happens when you ask for it"
+        ec.print((* readerfunc)(pins_to_read[i]));
+        ec.print("}");
+    }
+    ec.print("]}");
+}
+
+
+
+// GET handler for analog or digital reading
+//
+// Reads a single pin as specified in the tail of the URL.
+//
+// Skip the leading part (as defined by the urlfunc entry) and process
+// the pin number, take it as gospel. No sanity is enforced, you get what
+// you ask for whether that makes sense or not.
+//
+// Return a JSON that looks like
+//
+// { "pins" : [ { "pin" : nn, "value" : vv } ], ... }
+//
+// Note that there are other elements in this object (e.g., serverVersion)
+// so the client must JSON parse the returned object accordingly.
+//
+// This may seem overly general but conforms to the format used for
+// the multiple pin read operations as well.
+//
+void
+do_one_read(struct http_rq *rp, struct urlfuncs *ufp, int (*rf)(uint8_t))
+{
+    int pinnum = atoi(&rp->bufp[strlen(ufp->url)]);
+
+    // we just fake up a JSON request... a little hokey but here we go
+    jsmntok_t token;
+
+    token.type = JSMN_STRING;
+    token.start = strlen(ufp->url);
+    token.end = strlen(rp->bufp);
+    token.size = 0;      // a STRING "contains" no other elements
+
+    processjsonreads(rp->client, &token, rp->bufp, rf);
+}
+
+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)
+{
+    do_one_read(rp, ufp, digitalRead);
+}
+
+
+
+
+// POST handler for analog/digital reading
+//
+// Reads multiple pins as specified in the POSTed JSON which
+// will either be a single pin:
+//               { "pins" : nn }
+//
+// or multiple:  { "pins" : [ nn, mm, xx, yy ] }
+//
+// No sanity is enforced on pin numbers, you get what you ask for.
+//
+// Response will look like:
+// { "pins" : [ { "pin" : nn, "value" : vv }, ... ], ... }
+//
+// 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))
+{
+    int i;
+    int n;
+    jsmn_parser parser;
+
+// given a maximum number of pins, how many tokens we need for you
+// to be able to "say" that many (with slop)
+    jsmntok_t tokens[MAXJSMNTOKENS];
+
+    // 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) == -1) {
+
+        rp->client.print(reply400);
+        return;
+    }
+
+    jsmn_init(&parser);
+    n = jsmn_parse(&parser, rp->bufp, strlen(rp->bufp), tokens, MAXJSMNTOKENS);
+    i = ((n > 0) ? jsmn_findkeyval(tokens, rp->bufp, "pins") : -1);
+
+    if (i == -1) {
+        // didn't find it
+        rp->client.print(reply400);
+        return;
+    }
+
+    // i is the value token index
+    processjsonreads(rp->client, &tokens[i], rp->bufp, rf);
+}
+
+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)
+{
+    do_json_reads(rp, ufp, digitalRead);
+}
+
+// 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\":");
+    rp->client.print(requests_processed);
+    rp->client.print(",\"uptime_msecs\":");
+    rp->client.print(millis());
+    rp->client.print("}");
+}
+
+// This is a somewhat-generic outline for handling several similar
+// types of POST requests.
+//
+// The theory here is that we have an object or an array that looks
+// like this, as an example using the pinmode API:
+//
+//             { "modes" : { "pin" : pp, "mode" : mm } }
+//        or   { "modes" : [ { "pin" : pp, "mode" : mm }, ... ] }
+//
+// But the digitalWrite API uses the same general outline, except
+// that instead of "pinmode" it says "digitalwrite" and instead of
+// "mode" it says "value", and the list of allowed substitutions
+// (e.g., HIGH/LOW) is different. And, of course, the function to
+// be called is different.
+//
+// So this is a generic routine that handles all that. I could have
+// 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.
+//
+
+struct pinop_params {
+    char *topobj;           // e.g., "modes"
+    void (* callback)(int, int, struct pinop_params *);
+    void *callback_data;
+    char *tokbuf;
+    char *key1;              // e.g., "pin"
+    char *key2;              // e.g., "mode" or "value"
+};
+
+
+int
+pinops(struct pinop_params *parms)
+{
+    int n;
+    int i;
+    jsmn_parser parser;
+    jsmntok_t tokens[MAXJSMNTOKENS];
+    jsmntok_t *tp = NULL;
+    char *bufp = parms->tokbuf;   // just a shorthand
+
+    jsmn_init(&parser);
+    n = jsmn_parse(&parser, bufp, strlen(bufp), tokens, MAXJSMNTOKENS);
+    if (n < 1 || (i = jsmn_findkeyval(tokens, bufp, parms->topobj)) == -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--) {
+        int ix1 = jsmn_findkeyval(tp, parms->tokbuf, parms->key1);
+        int ix2 = jsmn_findkeyval(tp, parms->tokbuf, parms->key2);
+
+        // we're just silently ignoring entries without necessary fields
+        if ((ix1 != -1) && (ix2 != -1)) {
+            int v1 = jsmn_atoi(&tp[ix1], parms->tokbuf);
+            int v2;
+
+            jsmntok_t *tpm = &tp[ix2];
+            if (tpm->type == JSMN_STRING) {
+                if (jsmn_strcmp(tpm, "OUTPUT", parms->tokbuf) == 0)
+                    v2 = OUTPUT;
+                else if (jsmn_strcmp(tpm, "INPUT", parms->tokbuf) == 0)
+                    v2 = INPUT;
+                else if (jsmn_strcmp(tpm, "INPUT_PULLUP", parms->tokbuf) == 0)
+                    v2 = INPUT_PULLUP;
+                else if (jsmn_strcmp(tpm, "HIGH", parms->tokbuf) == 0)
+                    v2 = HIGH;
+                else if (jsmn_strcmp(tpm, "LOW", parms->tokbuf) == 0)
+                    v2 = LOW;
+                else
+                    v2 = jsmn_atoi(tpm, parms->tokbuf);
+            } else {
+                v2 = jsmn_atoi(tpm, parms->tokbuf);
+            }
+            (* parms->callback)(v1, v2, parms);
+            tp += jsmn_skiptok(tp);
+        }
+    }
+    return 0;
+}
+
+// the callback for pinmode interface
+void
+call_pinmode(int pin, int mode, struct pinop_params *ignored)
+{
+    pinMode(pin, mode);
+}
+
+// POST handler for configuring pinmodes
+//
+// Instructions for calling pinMode are in the POST:
+//
+//     { "modes" : { "pin" : pp, "mode" : mm } }
+// or
+//     { "modes" : [ { "pin" : pp, "mode" : mm }, ... ] }
+//
+// The second form allows multiple pinmodes per single HTTP operation
+//
+// We accept numbers for mode or: "OUTPUT", "INPUT", "INPUT_PULLUP"
+//
+// Return is just an empty 200 / OK
+//
+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 :)
+    if (rp->content_length > rp->bufsiz-1 ||
+        clientgetn(rp->client, rp->bufp, rp->content_length) == -1) {
+        rp->client.print(reply400);
+        return;
+    }
+
+    struct pinop_params px;
+    px.topobj = "modes";
+    px.callback = call_pinmode;
+    px.callback_data = NULL;
+    px.tokbuf = rp->bufp;
+    px.key1 = "pin";
+    px.key2 = "mode";
+
+    if (pinops(&px) == 0)
+        rp->client.print(httpreply1);
+    else
+        rp->client.print(reply400);
+}
+
+// the callback for digitalWrite interface
+void
+call_digwrite(int pin, int val, struct pinop_params *ignored)
+{
+    digitalWrite(pin, val);
+}
+
+// POST handler for digitalWrites
+//
+// Instructions for calling digitalWrite are in the POST:
+//
+//     { "writes" : { "pin" : pp, "value" : mm } }
+// or
+//     { "writes" : [ { "pin" : pp, "value" : mm }, ... ] }
+//
+// The second form allows multiple pinmodes per single HTTP operation
+//
+// We accept numbers for values or: "HIGH", "LOW"
+//
+// Return is just an empty 200 / OK
+
+
+
+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 :)
+    if (rp->content_length > rp->bufsiz-1 ||
+        clientgetn(rp->client, rp->bufp, rp->content_length) == -1) {
+        rp->client.print(reply400);
+        return;
+    }
+
+    struct pinop_params px;
+    px.topobj = "writes";
+    px.callback = call_digwrite;
+    px.callback_data = NULL;
+    px.tokbuf = rp->bufp;
+    px.key1 = "pin";
+    px.key2 = "value";
+
+    if (pinops(&px) == 0)
+        rp->client.print(httpreply1);
+    else
+        rp->client.print(reply400);
+}
+
+void
+send_badrequest(struct http_rq *rp, struct urlfuncs *ufp)
+{
+    rp->client.print(reply400);
+}
+
+// -----------
+// URL scheme
+// -----------
+//
+// This table defines the URL scheme and connects them to handlers.
+// Obviously you could extend this (or trim it!)
+//
+// GET /v1/analogRead/NNNN
+//    Returns (as JSON) an analogRead() of pin NNNN.
+//
+// POST /v1/analogRead
+//    Performs an analogRead on one or more pins specified via JSON
+//
+// POST /v1/configure/pinmode
+//    Configure pins via pinMode() from POSTed JSON info
+//
+// 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 }};
+
+
+//
+// called by the main loop to check and process web requests periodically
+//
+void
+webProcessing(EthernetClient& ec)
+{
+    //
+    //
+    // As is often the case with a small embedded device, we aren't
+    // necessarily fully parsing everything in a strictly conformant manner.
+    // Everything works if you do things correctly; if your client sends
+    // incorrectly formatted data the results are undefined (hopefully we
+    // won't crash, but there's no guarantee all format errors are acted upon)
+    //
+
+    char sbuf[REQUESTBUFFERSIZE];     // XXX maybe should malloc
+    struct http_rq rq(ec);
+
+    rq.bufp = sbuf;
+    rq.bufsiz = REQUESTBUFFERSIZE;
+    rq.t0 = millis();                // time zero for enforcing timeouts
+
+    if (parseHTTPheader(&rq) != -1) {
+        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;
+                }
+            }
+        }
+        (* dtp->f)(&rq, dtp);
+        ++requests_processed;
+    }
+}
+
+
+
+// the setup routine runs once when you press reset:
+void setup()
+{
+    Ethernet.begin(mac);
+    server.begin();
+}
+
+//
+// the loop routine is called over and over again
+//
+
+void loop()
+{
+    // DHCP lease check/renewal (library only sends request if expired)
+    Ethernet.maintain();
+
+    // check for web requests and do them
+    EthernetClient client = server.available();
+    if (client) {
+       webProcessing(client);
+       client.stop();
+    }
+}


function organization

function organization

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Au1st3in/fall2015-e121-arduino-code.git/'


Accel Testing Code

Accel Testing Code

the current sensor packages. This is running on the arduino and dumps
gyro/accel to the sd.

error: pathspec 'Calibration/accel_skeleton/accel_test/accel_test.ino' did not match any file(s) known to git

--- Calibration/accel_skeleton/accel_test/accel_test.ino
+++ Calibration/accel_skeleton/accel_test/accel_test.ino
@@ -0,0 +1,166 @@
+/*
+*  Accelerometer skeleton code
+*
+*/
+#include <SD.h>
+#include <SPI.h>
+#include <Wire.h>
+#include <String.h>
+#include <Adafruit_Sensor.h>
+#include <Adafruit_LSM303_U.h>
+#include <Adafruit_10DOF.h>
+
+/* Assign a unique ID to the sensors */
+Adafruit_10DOF                dof   = Adafruit_10DOF();
+Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(30301);
+
+void initAccel()
+{
+  if(!accel.begin())
+  {
+    /* There was a problem detecting the LSM303 ... check your connections */
+    while(1);
+  }
+}
+
+/**************************************************************************/
+/*!
+
+*/
+/**************************************************************************/
+void setup(void)
+{
+  //this is needed for the duemilanove and uno without ethernet shield
+  const int sdCardPin = 10;
+  delay(1000);
+  pinMode(10,OUTPUT);
+  if (!SD.begin(sdCardPin)) {
+    // don't do anything more:
+    return;
+  }
+  //format
+  sdWrite("AccelX, AccelY, AccelZ, Roll, Pitch, Heading");
+  sdWriteNewline();
+  /* Initialise the sensors */
+  initAccel();
+}
+
+/**************************************************************************/
+/*!
+    @brief  Constantly check the roll/pitch/heading/altitude/temperature
+*/
+/**************************************************************************/
+void loop(void)
+{
+  sensors_event_t accel_event;
+  sensors_vec_t   orientation;
+  //16 bits long because thats what floatToString takes
+  char dataArray[16];
+  /* Calculate pitch and roll from the raw accelerometer data */
+  accel.getEvent(&accel_event);
+  if (dof.accelGetOrientation(&accel_event, &orientation))
+  {
+    floatToString(dataArray, accel_event.acceleration.x, 3, 5);
+    sdWrite(dataArray);
+    sdWrite(", ");
+    floatToString(dataArray, accel_event.acceleration.y, 3, 5);
+    sdWrite(dataArray);
+    sdWrite(", ");
+    floatToString(dataArray, accel_event.acceleration.z, 3, 5);
+    sdWrite(dataArray);
+    sdWrite(", ");
+    floatToString(dataArray, orientation.roll, 3, 5);
+    sdWrite(dataArray);
+    sdWrite(", ");
+    floatToString(dataArray, orientation.pitch, 3, 5);
+    sdWrite(dataArray);
+    sdWrite(", ");
+    floatToString(dataArray, orientation.heading, 3, 5);
+    sdWrite(dataArray);
+    sdWriteNewline();
+  }
+  delay(5);
+}
+bool sdWriteNewline() {
+  File dataFile = SD.open("run2-13.txt", FILE_WRITE);
+  // if the file is available, write to it:
+  if (dataFile) {
+    dataFile.println("");
+    dataFile.close();
+    // print to the serial port too:
+  }
+  // if the file isn't open, pop up an error:
+  else {
+    dataFile.close();
+  }
+}
+
+bool sdWrite(String data) {
+  File dataFile = SD.open("run2-13.txt", FILE_WRITE);
+  // if the file is available, write to it:
+  if (dataFile) {
+    dataFile.print(data);
+    dataFile.close();
+    // print to the serial port too:
+  }
+  // if the file isn't open, pop up an error:
+  else {
+    dataFile.close();
+  }
+}
+char * floatToString(char * outstr, double val, byte precision, byte widthp){
+ char temp[16]; //increase this if you need more digits than 15
+ byte i;
+
+ temp[0]='\0';
+ outstr[0]='\0';
+
+ if(val < 0.0){
+   strcpy(outstr,"-\0");  //print "-" sign
+   val *= -1;
+ }
+
+ if( precision == 0) {
+   strcat(outstr, ltoa(round(val),temp,10));  //prints the int part
+ }
+ else {
+   unsigned long frac, mult = 1;
+   byte padding = precision-1;
+
+   while (precision--)
+     mult *= 10;
+
+   val += 0.5/(float)mult;      // compute rounding factor
+
+   strcat(outstr, ltoa(floor(val),temp,10));  //prints the integer part without rounding
+   strcat(outstr, ".\0"); // print the decimal point
+
+   frac = (val - floor(val)) * mult;
+
+   unsigned long frac1 = frac;
+
+   while(frac1 /= 10)
+     padding--;
+
+   while(padding--)
+     strcat(outstr,"0\0");    // print padding zeros
+
+   strcat(outstr,ltoa(frac,temp,10));  // print fraction part
+ }
+
+ // generate width space padding
+ if ((widthp != 0)&&(widthp >= strlen(outstr))){
+   byte J=0;
+   J = widthp - strlen(outstr);
+
+   for (i=0; i< J; i++) {
+     temp[i] = ' ';
+   }
+
+   temp[i++] = '\0';
+   strcat(temp,outstr);
+   strcpy(outstr,temp);
+ }
+
+ return outstr;
+}


Implement a simple motion detection

Implement a simple motion detection

--- ledtest2.ino
+++ ledtest2.ino
@@ -2,6 +2,13 @@
 int clock = 5; // SCK pin
 int latch = 6; // RCK pin
 int data = 7;  // DIO pin
+
+/*
+looking at the edge with contacts, having the sensor pointing to the front, the pins are from left to right: Ground, Signal, +5V
+*/
+int motionPin = 3;
+int lightPin = A0;
+int speakerPin = 11;

 #include "led8.h"

@@ -13,47 +20,40 @@

 void setup()
 {
+  pinMode(motionPin, INPUT);
+  pinMode(speakerPin, OUTPUT);
+
   led.init(data, clock, latch);
   led.clear();

   led.moveToStart();
-  led.print(0);
-  led.print(1);
-  led.print(2);
-  led.print(3);
-  led.print(4);
-  led.print(5);
-  led.print(6);
-  led.print(7);
   led.clear();
   led.print(0, "A");
-  led.print(4, "B");
+  led.print(4, "L");
 }

 void loop()
 {
+  int m = digitalRead(motionPin);
+  int l = map(analogRead(lightPin), -1023, 1023, -99, 99);

-  led.moveToStart();
+  led.print(1, m, 2);
+  led.print(5, l, 3);

-  led.print(1, 12, 3);
-  led.print(5, -34, 3);
-  led.tick(0);
-//  int mils = millis();
-//  if (mils - lastmils < del)
-//    return;
-//  lastmils = mils;
-//
-//  led.moveToStart();
-//  for (int i = start; i < 8 + start; i++)
-//    led.print(String(i));
-//
-//  if (dir > 0)
-//    if (start < 8)
-//      start++;
-//    else
-//      dir = -1;
-//  else if (start > 0)
-//    start--;
-//  else
-//    dir = 1;
+  if (l > 60)
+  {
+    if (m == HIGH)
+    {
+      digitalWrite(speakerPin, HIGH);
+      delay(1);
+      digitalWrite(speakerPin, LOW);
+      delay(1);
+    }
+  }
+
+  for (int i=0;i<100;i++)
+  {
+    led.tick(0);
+    delay(1);
+  }
 }


Added file fasnachtMODE2.ino to communicate 4 times faster but without dimming. see README.md

error: pathspec 'arduino/fasnacht/fasnachtMODE2.ino' did not match any file(s) known to git

--- arduino/fasnacht/fasnachtMODE2.ino
+++ arduino/fasnacht/fasnachtMODE2.ino
@@ -0,0 +1,157 @@
+// Written for SparkFun EL Sequencer
+// https://www.sparkfun.com/products/11323
+
+void setup() {
+    // The EL channels are on pins 2 through 9
+    // Initialize the pins as outputs
+    pinMode(0, INPUT);
+    pinMode(1, INPUT);
+    pinMode(2, OUTPUT);    // channel A
+    pinMode(3, OUTPUT);    // channel B
+    pinMode(4, OUTPUT);    // channel C
+    pinMode(5, OUTPUT);    // channel D
+    pinMode(6, OUTPUT);    // channel E
+    pinMode(7, OUTPUT);    // channel F
+    pinMode(8, OUTPUT);    // channel G
+    pinMode(9, OUTPUT);    // channel H
+    // We also have two status LEDs, pin 10 on the Escudo,
+    // and pin 13 on the Arduino itself
+    pinMode(10, OUTPUT);
+    pinMode(13, OUTPUT);
+    Serial.begin(9600);
+    Serial.println("Fasnacht 2014");
+}
+
+void loop()
+{
+    unsigned long start,        //time when sampling started
+                  finish,       //time when sampling ended
+                  time,         //sampling time
+                  ValueCount,   //counts Value average crossings
+                  ChannelCount, //counts Channel average crossings
+                  ValueFrq,     //Final Input freq of Value data
+                  ChannelFrq;   //Final Input freq of Channel data
+
+    unsigned int Ctr,           //counter
+                 ValueState,    //set if Value is bigger than its Average
+                 ChannelState,  //set if Channel is bigger than its Average
+                 ValueLast,     //last state of ValuePassed
+                 ChannelLast,   //last state of ChannelPassed
+                 Value,         //analog input of data
+                 Channel = 0,       //analog input of channel
+                 ValueAvg,      //voltage average
+                 ChannelAvg,    //voltage average
+                 ValueHighest = 0,     //peak value
+                 ChannelHighest = 0,   //peak value
+                 ValueLowest = 1023,   //peak value
+                 ChannelLowest = 1023; //peak value
+
+    int brightness; //brighness data
+
+    digitalWrite(13, 1);
+
+    start = millis();  //get arduino uptimes (unsigned long): max 50days :P
+    ValueCount = 0;
+    ChannelCount = 0;
+
+    for (Ctr = 0; Ctr < 200; Ctr++) {
+        //get input voltage
+        Value = analogRead(3);   // 2 = Audio left -> data
+        Channel = analogRead(2); // 2 = Audio right -> channel
+
+        //save peak values
+        if (Value > ValueHighest) ValueHighest = Value;
+        if (Value < ValueLowest)  ValueLowest = Value;
+        if (Channel > ChannelHighest) ChannelHighest = Channel;
+        if (Channel < ChannelLowest)  ChannelLowest = Channel;
+
+        //calculate voltage average
+        ValueAvg = (ValueHighest + ValueLowest) / 2;
+        ChannelAvg = (ChannelHighest   + ChannelLowest) / 2;
+
+        //check if input is higher or lower than its average
+        if (Value > ValueAvg) ValueState = 1;
+        else ValueState = 0;
+        if (Channel > ChannelAvg) ChannelState = 1;
+        else ChannelState = 0;
+
+        //count number of average crossings
+        if (ValueState != ValueLast) ValueCount++;
+        if (ChannelState != ChannelLast) ChannelCount++;
+
+        //save state
+        ValueLast = ValueState;
+        ChannelLast = ChannelState;
+    }
+
+    //get sampling time
+    finish = millis();
+    time = finish - start;
+
+    //calculate freq
+    ValueFrq = ValueCount * 100 / time / 2 * 10;
+    ChannelFrq = ChannelCount * 100 / time / 2 * 10;
+
+    //get channel data and write output LEFT AUIO CHANNEL
+    if( (ValueFrq > 140) && (ValueFrq <= 160) ) {
+      digitalWrite(3, 0); //B
+      digitalWrite(5, 0); //D
+      Serial.print("B OFF D OFF ");
+    }
+    if( (ValueFrq > 240) && (ValueFrq <= 260) ) {
+      digitalWrite(3, 1); //B
+      digitalWrite(5, 0); //D
+      Serial.print("B ON  D OFF ");
+    }
+    if( (ValueFrq > 340) && (ValueFrq <= 360) ) {
+      digitalWrite(3, 0); //B
+      digitalWrite(5, 1); //D
+      Serial.print("B OFF D ON  ");
+    }
+    if( (ValueFrq > 440) && (ChannelFrq <= 460) ) {
+      digitalWrite(3, 1); //B
+      digitalWrite(5, 1); //D
+      Serial.print("B ON  D ON  ");
+    }
+
+    //get channel data and write output RIGHT AUIO CHANNEL
+    if( (ChannelFrq > 140) && (ChannelFrq <= 160) ) {
+      digitalWrite(6, 0); //E
+      digitalWrite(9, 0); //H
+      Serial.println("E OFF H OFF ");
+    }
+    if( (ChannelFrq > 240) && (ChannelFrq <= 260) ) {
+      digitalWrite(6, 1); //E
+      digitalWrite(9, 0); //H
+      Serial.println("E ON  H OFF ");
+    }
+    if( (ChannelFrq > 340) && (ChannelFrq <= 360) ) {
+      digitalWrite(6, 0); //E
+      digitalWrite(9, 1); //H
+      Serial.println("E OFF H ON  ");
+    }
+    if( (ChannelFrq > 440) && (ChannelFrq <= 460) ) {
+      digitalWrite(6, 1); //E
+      digitalWrite(9, 1); //H
+      Serial.println("E ON  H ON  ");
+    }
+
+    /*
+    Serial.println("");
+    //e
+    Serial.print("Channel: ");
+    Serial.println(ChannelFrq);
+    Serial.print("Value: ");
+    Serial.println(ValueFrq);
+    Serial.print("Brightness: ");
+    Serial.println(brightness);
+    */
+    Serial.println("---------------");
+
+
+//    analogWrite(3, brightness);
+//    analogWrite(5, brightness);
+
+    digitalWrite(13, 0);
+}
+


Delete beestation - Copy (3).ino

Delete beestation - Copy (3).ino

Delete unwanted cruft

/bin/sh: 1: Syntax error: "(" unexpected


Added gCnBridge3 with Arduino/Atmega/Spark Core support

error: pathspec 'gcnbridge3/gcnbridge3.ino' did not match any file(s) known to git

--- gcnbridge3/gcnbridge3.ino
+++ gcnbridge3/gcnbridge3.ino
@@ -0,0 +1,1480 @@
+/*
+	              _____ gcnBridge _____
+	                   \---------/
+	Arduino globalCALCnet-to-CALCnet Bridge Software
+
+	(c) 2003-2014 Christopher Mitchell / "Kerm Martian"
+
+	http://www.cemetech.net
+	christoper@cemetech.net
+
+	Connects PC-side gCn client to one or more TI graphing calculators on
+	a CALCnet2.2 network.
+
+ */
+
+//ONLY ONE OF THESE
+#define avr328
+//#define avr168
+
+//ONLY ONE OF THESE
+#define sparkcore
+//#define ardubridge
+//#define usbhid
+
+#ifdef sparkcore            // Spark Core implies Ardubridge
+#define ardubridge
+//#define sparkdebug          // Toggle this as necessary
+SYSTEM_MODE(SEMI_AUTOMATIC);
+
+const char* server_host = "gcnhub.cemetech.net";
+uint16_t server_port = 4295;
+char localhub[17] = "Spark0000";
+char remotehub[17] = "IRCHub";
+const char* hexstring = "0123456789ABCDEF";
+
+const int TCP_MSG_BUF_SIZE = 300;
+uint8_t msg_buf[TCP_MSG_BUF_SIZE];
+
+// LED defines
+const int ledtoggle = 7;
+uint8_t rgbledmask = 0;
+const int ledr = 1 << 0;
+const int ledg = 1 << 1;
+const int ledb = 1 << 2;
+
+// Commands and addresses
+const uint8_t self_addr[5] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+#define EEPROM_MAGIC_OFFSETL 0
+#define EEPROM_MAGIC_OFFSETR 1
+#define EEPROM_LOCALHUB_OFFSET 2
+#define EEPROM_REMOTEHUB_OFFSET 19
+#define EEPROM_END_OFFSET 36
+#define EEPROM_MAGIC 42
+bool connectable = false;                   // Don't even try if we're not configured
+#else
+
+const int ledr = 8;
+const int ledg = 9;
+const int ledb = 10;
+#define setRGBLED(mask, state) digitalWrite(mask, state)
+
+#endif
+
+#ifdef usbhid
+extern "C" unsigned int usbFunctionSetup(unsigned char data[8]);
+extern "C" unsigned char usbFunctionWrite(unsigned char *data, unsigned char len);
+#define USB_CFG_DESCR_PROPS_STRING_VENDOR USB_PROP_LENGTH(8)
+#define USB_CFG_VENDOR_NAME {'C','e','m','e','t','e','c','h'}
+#define USB_CFG_VENDOR_NAME_LEN 8
+#define USB_CFG_DESCR_PROPS_STRING_PRODUCT USB_PROP_LENGTH(9)
+#define USB_CFG_DEVICE_NAME {'g','C','n','B','r','i','d','g','e'}
+#define USB_CFG_DEVICE_NAME_LEN 9
+#include "usbconfig_gcnbridge.h"
+#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+#include <string.h>
+#include <avr/io.h>
+#include <avr/wdt.h>
+#include <avr/interrupt.h>  /* for sei() */
+#include <util/delay.h>     /* for _delay_ms() */
+#include <avr/eeprom.h>
+
+#include <avr/pgmspace.h>   /* required by usbdrv.h */
+//*****CHRISTOPHER'S NOTES: Rename usbdrv_gcnbridge.h to usbdrv.h for this project.
+//*****WILL NEED to change name of other usbdrv.h files for other projects.
+//*****DITTO with oddebug.h!
+#include "usbdrv.h"
+#include "oddebug.h"        /* This is also an example for using debug macros */
+
+uint8_t output_pending;      //1 for a frame, 2 for the reset
+uint8_t input_pending;
+unsigned char resetstring[4] = {
+  ':', 'R', 's', '\n'};
+#endif
+
+#ifdef usbhid
+const int TIring = 11;
+const int TItip = 12;
+#else
+const int TIring = 5;
+const int TItip = 6;
+uint8_t led_state = 0;
+#endif
+
+//Global frame send/receive data
+uint8_t framebuffer[1+256+2+5+5];        //1 byte USB prefix!f
+uint8_t* sender = framebuffer + 1+0;  //5 bytes
+uint8_t* recipient = framebuffer + 1+5;  //5 bytes
+uint8_t* datasizeptr = framebuffer+1+10;  //2 bytes
+uint8_t* data = framebuffer + 1+12;    //256 bytesd
+
+//Global network constituent storage/searching
+#ifdef avr328
+#define calcstoresize 500
+#else
+#define calcstoresize 200
+#endif
+
+uint8_t* calcsearchptr_recv;
+uint8_t* calcsearchptr_send;
+uint8_t calcsknown;
+uint8_t calcspassed_recv;
+uint8_t calcspassed_send;
+uint8_t calcsearchstate;
+uint8_t calcstore[calcstoresize];
+
+#define TIwhite TIring
+#define TIred TItip
+#define CN2clock TItip
+#define CN2data TIring
+
+#define ERR_READ_TIMEOUT 1000
+#define ERR_WRITE_TIMEOUT 2000
+#define TIMEOUT 4000
+#define GET_ENTER_TIMEOUT 30000
+
+#ifdef sparkcore
+
+#define CN2_GETBYTE1_TIMEOUT 2000 //is 40 oncalc
+#define CN2_GETBYTE3_TIMEOUT 2000 //is 40 oncalc
+#define CN2_GETBYTE5_TIMEOUT 9000 //is 40 oncalc
+#define CN2_RECEIVEFRAMESTARTING_TIMEOUT 9000000 //(55000-6000)/58 oncalc
+
+#define CN2_GETBYTE1_TIMEOUT_US 230
+#define CN2_GETBYTE3_TIMEOUT_US 200
+#define CN2_GETBYTE5_TIMEOUT_US 430
+#define CN2_RECEIVEFRAMESTARTING_TIMEOUT_US 15000
+
+#else
+
+#define CN2_GETBYTE1_TIMEOUT 200 //is 40 oncalc
+#define CN2_GETBYTE3_TIMEOUT 200 //is 40 oncalc
+#define CN2_GETBYTE5_TIMEOUT 900 //is 40 oncalc
+#define CN2_RECEIVEFRAMESTARTING_TIMEOUT 900000 //(55000-6000)/58 oncalc
+
+#endif
+
+#define CN2_MAX_FRAME_RETRIES 255
+
+#define WAITTIME(datasize) (17+(46*(datasize&0x7fff))/3)
+
+#ifdef sparkcore
+TCPClient client;
+#endif
+
+void setup() {
+
+    //Initialize pins
+    resetLines();
+
+    //debugging
+#ifndef sparkcore
+    pinMode(ledr,OUTPUT);
+    pinMode(ledg,OUTPUT);
+    pinMode(ledb,OUTPUT);
+#else
+    // RGB LEDs will be handled by on-board RGB LED
+	pinMode(ledtoggle,OUTPUT);
+#endif
+
+    calcsknown = 0;
+
+    // Initialize USB
+#ifdef usbhid
+    //Was //UsbStream.begin()
+    // disable timer 0 overflow interrupt (used for millis)
+    TIMSK0&=!(1<<TOIE0);
+    cli();
+    usbInit();
+    usbDeviceDisconnect();
+    unsigned char i;
+    i = 0;
+    while(--i){             /* fake USB disconnect for > 250 ms */
+        _delay_ms(1);
+    }
+    usbDeviceConnect();
+
+    input_pending = 2;
+    output_pending = 0;
+    sei();
+#endif
+
+    // Initialize serial communication
+#ifdef ardubridge
+#ifdef sparkcore
+    WiFi.on();                  // Vital in SEMI_AUTOMATIC mode
+
+    Serial.begin(9600);             // Used for logging
+#ifdef sparkdebug
+    while(!Serial.available()) SPARK_WLAN_Loop();
+    Serial.print("Beginning bridge process with connectable = ");
+    Serial.println(connectable);
+#endif
+
+    // This is VITAL to copy the array to RAM, because dumb Spark bug
+    WiFi.connect();
+
+    // Check if local and remote hub name are not yet saved
+    if (EEPROM.read(EEPROM_MAGIC_OFFSETL != EEPROM_MAGIC)) {
+        int len = strlen(localhub);
+        for(int i=0; i < len; i++) {
+            EEPROM.write(EEPROM_LOCALHUB_OFFSET + i, localhub[i]);
+        }
+        EEPROM.write(EEPROM_LOCALHUB_OFFSET + len, 0);            // Null terminator
+        EEPROM.write(EEPROM_MAGIC_OFFSETL, EEPROM_MAGIC);
+    }
+    if (EEPROM.read(EEPROM_MAGIC_OFFSETR != EEPROM_MAGIC)) {
+        int len = strlen(remotehub);
+        for(int i=0; i < len; i++) {
+            EEPROM.write(EEPROM_REMOTEHUB_OFFSET + i, remotehub[i]);
+        }
+        EEPROM.write(EEPROM_REMOTEHUB_OFFSET + len, 0);            // Null terminator
+        EEPROM.write(EEPROM_MAGIC_OFFSETR, EEPROM_MAGIC);
+    }
+#ifdef sparkdebug
+    Serial.print("Credentials = ");
+    Serial.print(WiFi.hasCredentials());
+    Serial.print("; magic_offsetl = ");
+    Serial.print(EEPROM.read(EEPROM_MAGIC_OFFSETL));
+    Serial.print("; magic_offsetr = ");
+    Serial.println(EEPROM.read(EEPROM_MAGIC_OFFSETR));
+#endif
+
+    // Figure out if we can connect yet
+    if (WiFi.hasCredentials() &&
+        (EEPROM.read(EEPROM_MAGIC_OFFSETL) == EEPROM_MAGIC) &&
+        (EEPROM.read(EEPROM_MAGIC_OFFSETR) == EEPROM_MAGIC))
+    {
+        connectable = true;
+    }
+
+    // Initialize stuff
+    if ((EEPROM.read(EEPROM_MAGIC_OFFSETL) == EEPROM_MAGIC) &&
+        (EEPROM.read(EEPROM_MAGIC_OFFSETR) == EEPROM_MAGIC))
+    {
+        copyHubsToRAM();
+    }
+    if (connectable) {
+        connectMetahub();               // At least attempt
+    }
+#else
+    Serial.begin(115200);           // Used to communicate with gCnClient
+    Serial.write(":RESETs");
+    Serial.write('\n');
+#endif
+#endif
+
+}
+
+void loop() {
+    int i,j;
+    uint16_t datasize;
+
+    resetLines();
+#ifdef usbhid
+    usbPoll();
+#endif
+    setRGBLED(ledr, LOW);
+    setRGBLED(ledg, LOW);
+    setRGBLED(ledb, LOW);
+
+#ifdef sparkcore
+    if (!client.connected()) {
+        setRGBLED(ledr, HIGH);
+    }
+#endif
+
+#ifdef usbhid
+    if (output_pending) {
+        j = 0;
+        usbPoll();      //try to ack
+        do {
+            setRGBLED(ledr, HIGH);
+            uint8_t oldSREG = SREG;
+            cli();
+            i = cn2_send();
+            SREG = oldSREG;
+            if (i != 0) {
+                setRGBLED(ledr, LOW);
+                if (i == -99) setRGBLED(ledg, HIGH);
+                    usbPoll();
+                j=40;
+                while(--j)             /* fake USB disconnect for > 250 ms */
+                    _delay_ms(1);
+                usbPoll();
+                j++;
+            }
+        }
+        while (i != 0 && j < CN2_MAX_FRAME_RETRIES);
+        output_pending = 0;
+        usbEnableAllRequests();
+    }
+
+#else
+    mySerialEmptyBuf();
+    bool doFlush = true;
+    int tempval;
+#ifndef sparkcore
+    if (Serial.available() > 0) {
+        if (255 != mySerialRead())
+        goto serialFlush;
+        if (137 != mySerialRead())
+        goto serialFlush;
+
+        digitalWrite(CN2clock, LOW); // TEMPORARY - inhibit Cn2.2
+        digitalWrite(ledtoggle, HIGH);
+
+        // Fetch the recipient ID
+        for(i=0; i<5; i++) {
+            if (-1 == (tempval = mySerialRead()))
+                goto serialFlush;
+            recipient[i] = tempval;
+        }
+        // Fetch the sender ID
+        for(i=0; i<5; i++) {
+            if (-1 == (tempval = mySerialRead()))
+                goto serialFlush;
+            sender[i] = tempval;
+        }
+        // Get the data size word
+        if (-1 == (tempval = mySerialRead()))
+            goto serialFlush;
+        datasize = datasizeptr[0] = tempval;
+
+        if (-1 == (tempval = mySerialRead()))
+            goto serialFlush;
+        datasizeptr[1] = 0x7f & (tempval);
+        datasize += 256*(0x7f & datasizeptr[1]);
+        for(i=0; i<datasize; i++) {
+            if (-1 == (tempval = mySerialRead()))
+                goto serialFlush;
+            data[i] = tempval;
+        }
+
+        // Check if we received the end-of-message marker
+        if (mySerialRead() == 42) {
+            doFlush = false;
+            digitalWrite(CN2clock, HIGH); // TEMPORARY - un-inhibit Cn2.2
+            j = 0;
+            mySerialEmptyBuf();
+            do {
+                setRGBLED(ledr, HIGH);
+                i = cn2_send();
+                if (i != 0) {
+                    setRGBLED(ledr, LOW);
+                    if (i == -99) setRGBLED(ledg, HIGH);
+                    delay(100);
+                    j++;
+                }
+                mySerialEmptyBuf();
+            } while (i != 0 && j < CN2_MAX_FRAME_RETRIES);
+        } else {
+            mySerialEmptyBuf();
+        }
+
+    serialFlush:
+        digitalWrite(CN2clock, HIGH); // TEMPORARY - un-inhibit Cn2.2
+        digitalWrite(ledtoggle, LOW);
+
+        do {
+            if ('s' == Serial.read()) {
+                Serial.write('s');
+                break;
+            }
+        } while(doFlush);
+    }
+    // Finished reading and handling frame from serial
+#else   // sparkcore
+    // Try to receive and handle frame from network
+    int c;
+    int idx = 0;
+    int framelen = 3;
+    bool valid = true;
+    if (client.connected() && (-1 != (c = client.read()))) {
+#ifdef sparkdebug
+        Serial.println("Receiving frame from gCn...");
+#endif
+        msg_buf[0] = c;
+        idx++;
+        while(idx < framelen) {
+            do {
+                c = client.read();
+                if (c == -1) SPARK_WLAN_Loop();
+            } while (c == -1);
+
+            //if (idx < 3)
+                msg_buf[idx] = c;
+            if (idx == 1) {
+                framelen = 3 + (((0x7f & msg_buf[1]) << 8) | (msg_buf[0]));
+            }
+            if (idx == 3 && c != 255) valid = false;
+            if (idx == 4 && c != 137) valid = false;
+            if (idx == framelen - 1 && c != 42) valid = false;
+
+            if (idx >= 5 && idx < 10) {
+                recipient[idx - 5] = c;
+            }
+            if (idx >= 10 && idx < 15) {
+                sender[idx - 10] = c;
+            }
+            if (idx >= 15 && idx < 17) {
+                datasizeptr[idx - 15] = c;
+            }
+            if (idx >= 17 && idx < framelen - 1) {
+                data[idx - 17] = c;
+            }
+
+            idx++;
+        }
+
+        if (valid && (msg_buf[2] == 'b' || msg_buf[2] == 'f')) {
+#ifdef sparkdebug
+            Serial.print("Have valid frame to send to Cn2, type ");
+            Serial.write(msg_buf[2]);
+            Serial.println("");
+#endif
+            int retries = 0;
+            do {
+                setRGBLED(ledr, HIGH);
+                i = cn2_send();
+                if (i != 0) {
+                    setRGBLED(ledr, LOW);
+                    if (i == -99) setRGBLED(ledg, HIGH);
+                    delay(100);
+                    retries++;
+                }
+            } while (i != 0 && retries < CN2_MAX_FRAME_RETRIES);
+
+        } else {
+#ifdef sparkdebug
+            Serial.print("Have INVALID frame to send to Cn2, type ");
+            Serial.write(msg_buf[2]);
+            Serial.println("");
+#endif
+        }
+    } else if (!client.connected() && connectable) {
+        connectMetahub();
+    }
+#endif
+#endif
+
+    resetLines();
+    if (digitalRead(CN2clock) == HIGH)
+        return;         // Nothing to read from the network
+
+    // Try to read from the network
+#ifdef usbhid
+    _delay_ms(1);
+#else
+    delayMicroseconds(1000); //aka 1 millisecond
+#endif
+    if (digitalRead(CN2clock) == LOW) {
+        setRGBLED(ledg, LOW);
+#ifdef usbhid
+        uint8_t oldSREG = SREG;
+        cli();
+#endif
+        cn2_receive();
+#ifdef usbhid
+        SREG = oldSREG;
+#else
+        led_state = 1 - led_state;
+        digitalWrite(ledtoggle, led_state);
+#endif
+        }
+    }
+
+#ifdef ardubridge
+unsigned int mySerialRead()  {
+    unsigned int received = 0, tempval, val = 0;
+
+SerialReadStart:
+    while(Serial.available() <= 0) {}
+    if ((tempval = Serial.read()) == 's') {
+        setRGBLED(ledr, HIGH);
+        setRGBLED(ledg, HIGH);
+        setRGBLED(ledb, HIGH);
+        Serial.write('s');
+        goto SerialReadStart;
+    }
+    setRGBLED(ledr, LOW);
+    setRGBLED(ledg, LOW);
+    setRGBLED(ledb, LOW);
+    val |= (tempval-'0'*(tempval<='9')-('A'-10)*(tempval>='A'));
+    if (received == 0)
+        val = ((val << 4) & 0x00f0);
+    received++;
+    if (received == 1)
+        goto SerialReadStart;
+    return val;
+}
+
+void mySerialEmptyBuf() {
+    if (0 >= Serial.available())
+        return;
+    if (Serial.peek() == 's') {
+        Serial.read();
+        Serial.write('s');
+    }
+}
+#endif
+
+void resetLines() {
+#ifdef sparkcore
+    pinMode(TIring, INPUT_PULLUP);
+    pinMode(TItip, INPUT_PULLUP);
+#else
+    pinMode(TIring, INPUT);           // set pin to input
+    digitalWrite(TIring, HIGH);       // turn on pullup resistors
+    pinMode(TItip, INPUT);            // set pin to input
+    digitalWrite(TItip, HIGH);        // turn on pullup resistors
+#endif
+}
+
+static int cn2_send() {
+      int i,j;
+      uint16_t datasize;
+      uint8_t readytosend;
+      uint16_t checksum,checksum2, checksum3;
+      uint8_t addror;
+    #ifdef usbhid
+      uint16_t timerlong, timerlong2;
+    #else
+      unsigned long timerlong, timerlong2;
+    #endif
+
+#ifdef sparkdebug
+    Serial.print("Sending cn2.2 frame to ");
+    for(int i=0; i < 5; i++) {
+        Serial.print(recipient[i], HEX);
+    }
+    Serial.write(" from ");
+    for(int i=0; i < 5; i++) {
+        Serial.print(sender[i], HEX);
+    }
+    Serial.print(" length ");
+    Serial.print((datasizeptr[1] & 0x007f) * 256 + datasizeptr[0], DEC);
+    Serial.println("");
+#endif
+
+      resetLines();
+      do {
+        readytosend = 0;
+    #ifdef usbhid
+        TCCR1A = 0; //start TIMER1
+        TCCR1B = (1 << CS10); //NO prescaling!
+        TCNT1 = timerlong = 0;  //reset count
+        while((TCNT1-timerlong) < (uint16_t)((F_CPU/1000000)*883) && TCNT1 < (uint16_t)((F_CPU/1000000)*2500)) {
+          if (digitalRead(CN2clock) == LOW) timerlong = TCNT1;
+        }
+        if (TCNT1 < (uint16_t)((F_CPU/1000000)*2500)) readytosend = 1;
+        if (!readytosend) _delay_ms(10);
+    #else
+        timerlong = timerlong2 = micros();
+        while(micros()-timerlong < 883 && micros()-timerlong2 < 2500) {
+          if (digitalRead(CN2clock) == LOW) timerlong = micros();
+        }
+        if (micros()-timerlong2 < 2500) readytosend = 1;
+        if (!readytosend) delay(10);  //100 attempts per second
+    #endif
+      } while(!readytosend);
+    #ifdef usbhid
+      TCCR1A = 0; //start TIMER1
+      TCCR1B = (1 << CS11); // div8 prescaling!
+      TCNT1 = 0;  //reset count
+    #else
+      timerlong = micros();
+    #endif
+      pinMode(TItip, OUTPUT);            // set pin to output
+      pinMode(TIring, OUTPUT);           // set pin to output
+    #ifdef usbhid
+      while(TCNT1 < (uint16_t)((F_CPU/1000000)*(9000/8))) {
+    #else
+      while(micros()-timerlong < 9000) {
+    #endif
+        digitalWrite(CN2clock, LOW);
+        digitalWrite(CN2data, LOW);
+      }
+
+        setRGBLED(ledb, HIGH);
+        setRGBLED(ledr, LOW);
+
+      addror = 0;
+      for (i=0; i<5; i++) {
+        addror |= recipient[i];
+        j = cn2_sendbyte(recipient[i]);
+        if (j < 0) return -1;
+      }
+    #ifdef usbhid
+      _delay_us(44);
+    #else
+      timerlong = micros();
+      while(micros()-timerlong < 44) {
+      }
+    #endif
+      for (i=0; i<5; i++) {
+        j = cn2_sendbyte(sender[i]);
+        if (j < 0) return -1;
+      }
+
+        setRGBLED(ledg, HIGH);
+        setRGBLED(ledb, LOW);
+
+      if (0 > cn2_sendbyte(datasizeptr[0])) return -1;
+      if (0 > cn2_sendbyte(datasizeptr[1])) return -1;
+      datasize = (datasizeptr[1] & 0x007f) * 256 + datasizeptr[0];
+      checksum = 0;
+      for (i=0; i<datasize; i++) {
+        checksum += data[i];
+        j = cn2_sendbyte(data[i]);
+        if (j < 0) return -1;
+      }
+      if (addror == 0) {
+        setRGBLED(ledg, HIGH);
+return 0;    //broadcast
+      }
+
+    #ifdef usbhid
+      int waittime = WAITTIME(datasize);
+      _delay_us((double)waittime);
+    #else
+      timerlong = micros();
+      int waittime = WAITTIME(datasize);
+      while(micros()-timerlong < waittime) {
+      }
+    #endif
+      if (0 > (j = cn2_recbyte())) return j;
+      checksum2 = (uint16_t)j;
+      if (0 > (j = cn2_recbyte())) return -1;
+      checksum2 += 256*(uint16_t)j;
+      if (checksum != checksum2) return -1;
+      if (0 > cn2_sendbyte(0xaa)) return -1;
+        setRGBLED(ledb, HIGH);
+return 0;
+    }
+
+
+static void cn2_receive() {
+    uint32_t i;
+    uint16_t k, datasize;
+    int16_t j;
+
+    uint16_t checksum,checksum2, checksum3;
+    uint8_t addror;
+#ifdef sparkcore
+    const uint8_t* selfaddr_offset;     // Check if msg is to us
+    bool selfmatch;                     // set if target addr is us
+#endif
+    unsigned long timerlong, timerlong2;
+
+    resetLines();
+#ifdef usbhid
+    if (input_pending || output_pending)    //don't overwrite input with output!
+        return;
+#endif
+
+    //Initialize searching state machine
+    calcsearchptr_recv = calcsearchptr_send = calcstore;
+    calcspassed_recv = 0;
+    calcspassed_send = 0;
+    calcsearchstate = 0;
+
+#ifdef sparkdebug
+    Serial.println("Receiving frame from Cn2");
+#endif
+
+    unsigned long now, then;
+    now = then = micros();
+    while (digitalRead(CN2clock) != 1 &&
+           ((now - then) < CN2_RECEIVEFRAMESTARTING_TIMEOUT_US))
+    {
+        now = micros();
+    }
+    if ((now - then) >= CN2_RECEIVEFRAMESTARTING_TIMEOUT_US) {
+        return;
+    }
+
+    addror = 0;
+    selfmatch = true;
+    selfaddr_offset = self_addr;
+
+    for(i=0; i<5; i++) {
+        j = cn2_recbyte();
+        if (j < 0)
+            break;
+        recipient[i] = (j & 0x00ff);
+        addror |= recipient[i];
+#ifdef sparkcore
+        selfmatch = selfmatch && (recipient[i] == *selfaddr_offset);
+        selfaddr_offset++;
+#endif
+    }
+
+    if (j < 0) {
+#ifdef usbhid
+//UsbStream.write('f');
+//UsbStream.write('\n');
+#else
+#ifdef sparkdebug
+        Serial.print(j);
+        Serial.print(' ');
+#endif
+        Serial.write("f\n");
+#endif
+        return;
+    }
+    for(i=0; i<5; i++) {
+        j = cn2_recbyte();
+        if (j < 0)
+            break;
+        sender[i] = (j & 0x00ff);
+    }
+
+    if (j < 0) {
+#ifdef usbhid
+        //UsbStream.write('f');
+        //UsbStream.write('\n');
+#else
+#ifdef sparkdebug
+        Serial.print(j);
+        Serial.print(' ');
+#endif
+        Serial.write("f\n");
+#endif
+        return;
+    }
+
+    datasize = datasizeptr[0] = cn2_recbyte();
+    datasizeptr[1] = (cn2_recbyte() & 0x7f);
+    datasize += 256 * (0x7f & datasizeptr[1]);
+    if (datasize > 255) {
+#ifdef usbhid
+    //UsbStream.write('f');
+    //UsbStream.write('\n');
+#else
+#ifdef sparkdebug
+        Serial.print(j);
+        Serial.print(' ');
+#endif
+        Serial.write("f\n");
+#endif
+        return;
+    }
+    setRGBLED(ledb, HIGH);
+    checksum = 0;
+    for(i=0; i<datasize; i++) {
+        j = cn2_recbyte();
+        if (j < 0) break;
+        data[i] = j;
+        checksum += j;
+        if (99 == cn2_search_FSM(1)) return;
+    }
+    setRGBLED(ledr, LOW);
+    if (j < 0) {
+#ifdef usbhid
+        //UsbStream.write('f');
+        //UsbStream.write('\n');
+#else
+#ifdef sparkdebug
+        Serial.print(j);
+        Serial.print(' ');
+#endif
+        Serial.write("f\n");
+#endif
+        return;
+    }
+    k = 2;
+    if (addror != 0) {				//deal with checksumming things
+#ifdef usbhid
+        TCCR1A = 0; //start TIMER1
+        TCCR1B = (1 << CS10); //NO prescaling!
+        TCNT1 = 0;  //reset count
+        uint16_t waittime = WAITTIME(datasize);
+#else
+        timerlong = micros();
+        int waittime = WAITTIME(datasize);
+#endif
+        k = cn2_search_FSM(1000);
+        if (k == 99) return;
+#ifdef usbhid
+        while(TCNT1 < (uint16_t)((F_CPU/1000000)*waittime)) {
+            //up to 4095us = 17+(46/3)*datasize
+            //= 4095*(3/46) < 266 bytes -> 265 bytes
+#else
+        while(micros()-timerlong < waittime) {
+#endif
+        }
+        if (k == 2) {
+          if (0 > cn2_sendbyte(checksum & 0x00ff)) {
+#ifdef sparkdebug
+            Serial.println("Failed to send checksum LSB");
+#endif
+            return;
+          }
+          if (0 > cn2_sendbyte((checksum >> 8) & 0x00ff)) {
+#ifdef sparkdebug
+            Serial.println("Failed to send checksum MSB");
+#endif
+            return;
+          }
+          if (0 > (j = cn2_recbyte())) {
+#ifdef sparkdebug
+            Serial.print("Failed to receive checksum ack: ");
+            Serial.println(j);
+#endif
+            return;
+          }
+          if (j != 0xAA) {
+#ifdef sparkdebug
+            Serial.println("Checksum was not acked by calculator.");
+#endif
+            return;
+          }
+        }
+      }
+#ifdef usbhid
+    input_pending = 1;
+#else
+#ifndef sparkcore
+    // Arduino serial communication
+    Serial.write('|');
+    for(i=0; i<5; i++) {
+        if (sender[i] < 16)
+            Serial.write('0');
+        Serial.print(sender[i],HEX);
+    }
+    Serial.write('.');
+    for(i=0; i<5; i++) {
+        if (recipient[i] < 16)
+            Serial.write('0');
+        Serial.print(recipient[i],HEX);
+    }
+    Serial.write(',');
+    if (datasize < 16)
+        Serial.write('0');
+    Serial.print(datasize & 0x00ff,HEX);
+    if (datasize < 4096)
+        Serial.write('0');
+    Serial.print((datasize>>8) & 0x007f,HEX);
+    Serial.write(':');
+    for(i=0; i<datasize; i++) {
+        if (data[i] < 16)
+            Serial.write('0');
+        Serial.print(data[i],HEX);
+    }
+    if (k == 2)
+        Serial.write('>');
+    Serial.write('\n');
+#else
+    int framelen = 3 + 12 + datasize;
+    // Spark Core communication
+    msg_buf[0] = framelen & 0x00ff;
+    msg_buf[1] = (framelen >> 8) & 0x00ff;
+    msg_buf[2] = (addror != 0)?'f':'b';
+    msg_buf[3] = 255;
+    msg_buf[4] = 137;
+    for(i = 0; i < 5; i++) {
+        msg_buf[5 + i] = recipient[i];
+    }
+    for(i = 0; i < 5; i++) {
+        msg_buf[10 + i] = sender[i];
+    }
+    msg_buf[15] = datasize & 0x00ff;
+    msg_buf[16] = (datasize >> 8) & 0x007f;
+    for(i = 0; i < datasize; i++) {
+        msg_buf[17 + i] = data[i];
+    }
+    msg_buf[17 + datasize] = 42;
+#ifdef sparkdebug
+    Serial.print("Have valid frame for gCn, length ");
+    Serial.println(3 + framelen);
+
+    Serial.print("Have frame to send to gcn, type ");
+    Serial.write(msg_buf[2]);
+    Serial.write(" length ");
+    Serial.println(3 + framelen);
+    for(int i=0; i < 3 + framelen; i++) {
+        Serial.print(msg_buf[i], HEX);
+        Serial.print(' ');
+    }
+    Serial.println(";;;");
+#endif
+
+    if (selfmatch) {
+        // Process message meant for us
+        Serial.println("Self-message received");
+        sparkProcessCommand();
+    } else {
+        client.write(msg_buf, 18 + datasize);
+    }
+#endif
+#endif
+    return;
+}
+
+    /*
+    #ifdef usbhid
+    void UsbHexPrint(uint8_t inbyte) {
+      uint16_t outbyte;
+      outbyte = (0x000f&(inbyte>>4))+'0';
+      if (outbyte > '9') outbyte += 'A'-'9'-1;
+      //UsbStream.write(outbyte);
+      outbyte = (0x000f&inbyte)+'0';
+      if (outbyte > '9') outbyte += 'A'-'9'-1;
+      //UsbStream.write(outbyte);
+    }
+    #endif
+    */
+
+static uint16_t cn2_search_FSM(uint16_t ticks) {
+      //Valid search states:
+      // 0 = searching for sender
+      // 1 = sender known, searching for receiver
+      // 2 = sender known, receiver unknown
+      // 3 = both sender & receiver known
+
+      int i;
+      uint8_t j;
+
+      while (ticks > 0) {
+        if (calcsearchstate > 1) break;
+        else if (calcsearchstate == 0) {		//searching for sender
+          if (calcspassed_send >= calcsknown || calcsearchptr_send[0] > sender[0]) {	//unknown
+            pinMode(CN2clock, OUTPUT);           // set pin to output
+            digitalWrite(CN2clock,LOW);
+            if (calcsknown-calcspassed_send > 0) {
+              for(i=(5*(calcsknown-calcspassed_send))-1;i>=0;i--)
+                calcsearchptr_send[i+5] = calcsearchptr_send[i];
+            }
+            memcpy(calcsearchptr_send,sender,5);
+            calcsknown++;
+
+#ifdef sparkcore
+            // Notify the gCn metahub
+            connectMetahub();
+            Serial.println("Detected new calculator");
+            msg_buf[0] = 10;
+            msg_buf[1] = 0;
+            msg_buf[2] = 'c';
+            for(int i=0; i < 5; i++) {
+                msg_buf[3 + i + i] = hexstring[sender[i] >> 4];
+                msg_buf[4 + i + i] = hexstring[sender[i] & 0x0f];
+            }
+            client.write(msg_buf, 3 + 10);
+#endif
+
+            //calcsearchstate = 1;
+            setRGBLED(ledr, HIGH);
+            setRGBLED(ledg, HIGH);
+            setRGBLED(ledb, LOW);
+#ifdef usbhid
+            usbPoll();
+            for(i=0; i<12; i++) {
+              j=40;
+              while(--j)             /* fake USB disconnect for > 250 ms */
+                _delay_ms(1);
+              usbPoll();
+            }
+#else
+            delay(400);
+#endif
+            digitalWrite(CN2clock,HIGH);
+            setRGBLED(ledb, HIGH);
+#ifdef usbhid
+            for(i=0; i<12; i++) {
+              j=40;
+              while(--j)             /* fake USB disconnect for > 250 ms */
+                _delay_ms(1);
+              usbPoll();
+            }
+#else
+            delay(400);
+#endif
+            setRGBLED(ledb, LOW);
+#ifdef usbhid
+            for(i=0; i<12; i++) {
+              j=40;
+              while(--j)             /* fake USB disconnect for > 250 ms */
+                _delay_ms(1);
+              usbPoll();
+            }
+#else
+            delay(400);
+#endif
+            return calcsearchstate = 99;
+          }
+          else {
+            for(i=0;i<6;i++) {
+              if (i==5) {
+                calcsearchstate = 1;
+                break;
+              }
+              if (calcsearchptr_send[i] != sender[i]) break;
+            }
+            calcspassed_send++;
+            calcsearchptr_send+=5;
+          }
+        }
+        else if (calcsearchstate == 1) {		//searching for receiver
+          if (calcspassed_recv >= calcsknown || calcsearchptr_recv[0] > recipient[0]) {	//unknown
+            calcsearchstate = 2;
+            return 2;
+          }
+          else {
+            for(i=0;i<6;i++) {
+              if (i==5) {
+                calcsearchstate = 3;
+                return 3;
+              }
+              if (calcsearchptr_recv[i] != recipient[i]) break;
+            }
+            calcspassed_recv++;
+            calcsearchptr_recv+=5;
+          }
+        }
+        ticks--;
+      }
+      return calcsearchstate;
+}
+
+static uint16_t cn2_sendbyte(uint8_t outbyte) {
+      int i;
+      int loopcntr;
+      uint8_t tempbyte;
+      unsigned long timerlong;
+
+      pinMode(TIring, OUTPUT);           // set pin to output
+      digitalWrite(TIring, HIGH);
+      pinMode(TItip, OUTPUT);            // set pin to output
+      digitalWrite(TItip, HIGH);
+      resetLines();
+      tempbyte = 0;
+      //Cn2_Int_SendByteRev:
+
+      for(i=0; i<8; i++) {
+        tempbyte |= (outbyte&0x01);
+        if (i !=7) tempbyte<<=1;
+        outbyte>>=1;
+      }
+      outbyte = tempbyte;
+
+    #ifdef usbhid
+      _delay_us(48);
+    #else
+      timerlong = micros();
+      while(micros()-timerlong < 48) {
+      } //52us
+    #endif
+
+      //Cn2_Int_SendByte1:
+    #ifdef usbhid
+      TCCR1A = 0; //start TIMER1
+      TCCR1B = (1 << CS10); //NO prescaling!
+      TCNT1 = 0;  //reset count
+      while(TCNT1 < (uint16_t)((F_CPU/1000000)*104)) {
+    #else
+      timerlong = micros();
+      while(micros()-timerlong < 104) {  //108 us
+    #endif
+        if ((digitalRead(TIring)<<1 | digitalRead(TItip)) != 0x03)
+          goto cn2_sendbyte_collide;
+      }
+
+      //Cn2_Int_SendByte2:
+      pinMode(TIring, OUTPUT);           // set pin to output
+      digitalWrite(TIring, LOW);
+      pinMode(TItip, OUTPUT);            // set pin to output
+      digitalWrite(TItip, LOW);
+    #ifdef usbhid
+      _delay_us(52);
+    #else
+      timerlong = micros();
+      while(micros()-timerlong < 52) {
+      } //52us
+    #endif
+      digitalWrite(CN2clock, HIGH);
+      digitalWrite(CN2data, HIGH);
+    #ifdef usbhid
+      _delay_us(43);
+    #else
+      timerlong = micros();
+      while(micros()-timerlong < 43) {
+      } //43us
+#endif
+
+    for(loopcntr=0; loopcntr<8; loopcntr++) {
+        digitalWrite(CN2data, (outbyte&0x01));
+        outbyte>>=1;
+        digitalWrite(CN2clock, HIGH);
+#ifdef usbhid
+        _delay_us(17);
+#else
+        timerlong = micros();
+        while(micros()-timerlong < 17) {
+        } //52us
+#endif
+        digitalWrite(CN2clock, LOW);
+#ifdef usbhid
+        _delay_us(35);
+#else
+        timerlong = micros();
+        while(micros()-timerlong < 35) {
+        } //52us
+#endif
+        digitalWrite(CN2clock, HIGH);
+        digitalWrite(CN2data, HIGH);
+#ifdef usbhid
+        _delay_us(52);
+#else
+        timerlong = micros();
+        while(micros()-timerlong < 52) {
+        } //52us
+#endif
+    }
+    return 0;
+
+cn2_sendbyte_collide:
+    pinMode(TIring, OUTPUT);           // set pin to output
+    pinMode(TItip, OUTPUT);            // set pin to output
+
+#ifdef usbhid
+    TCCR1A = 0; //start TIMER1
+    TCCR1B = (1 << CS10); //NO prescaling!
+    TCNT1 = 0;  //reset count
+    while(TCNT1 < (uint16_t)((F_CPU/1000000)*665)) {
+#else
+    timerlong = micros();
+    while(micros()-timerlong < 665) {
+#endif
+        digitalWrite(CN2clock, LOW);
+        digitalWrite(CN2data, HIGH);
+    }
+    digitalWrite(CN2clock, HIGH);
+    digitalWrite(CN2data, HIGH);
+    return -1;
+}
+
+static uint16_t cn2_recbyte() {
+    int i;
+    int loopcntr;
+    uint8_t inbyte;
+
+    resetLines();
+    i=0;
+    //Cn2_Int_GetByte1:
+    while(((digitalRead(TIring)<<1 | digitalRead(TItip)) != 0x00) && i<CN2_GETBYTE1_TIMEOUT) i++;
+    if (i>=CN2_GETBYTE1_TIMEOUT) return -99;
+    //Cn2_Int_GetByte2:
+
+    i=0;
+    //Cn2_Int_GetByte3:
+    while(((digitalRead(TIring)<<1 | digitalRead(TItip)) != 0x03) && i<CN2_GETBYTE3_TIMEOUT) i++;
+    if (i>=CN2_GETBYTE3_TIMEOUT) return -1;
+    //Cn2_Int_GetByte4:
+
+    inbyte=0;
+    loopcntr=0;
+    //Cn2_Int_GetByte5:
+    while(loopcntr<8 && i<CN2_GETBYTE5_TIMEOUT) {
+        i=0;
+        while((digitalRead(CN2clock) != 0) && i<CN2_GETBYTE5_TIMEOUT) i++;
+        if (i>=CN2_GETBYTE5_TIMEOUT) return -1;
+        inbyte |= digitalRead(CN2data);
+        if (loopcntr != 7) inbyte <<= 1;
+
+        i=0;
+        while((digitalRead(CN2clock) != 1) && i<CN2_GETBYTE5_TIMEOUT) i++;
+        loopcntr++;
+    }
+    if (i>=CN2_GETBYTE5_TIMEOUT) return -1;
+    return inbyte;
+}
+
+#ifdef usbhid
+#include "gcnbridge2_usb.cpp.inc"
+#endif
+
+#ifdef sparkcore
+void connectMetahub(void) {
+    if (client.connected())
+        return;
+
+    if (!WiFi.ready()) {
+        Serial.println("Connecting to WiFi");
+        if (WiFi.connecting())
+            return;                     // Wait for it to connect
+        if (!WiFi.hasCredentials()) {     // Wait for credentials
+            Serial.println("No credentials");
+            return;
+        }
+        Serial.println("Starting connect.");
+        WiFi.connect();
+        return;                         // We'll get back here eventually
+    }
+
+    if (!client.connect(server_host, server_port)) {
+        Serial.write("Failed to connect.\n");
+        return;
+    }
+
+    // Connected! Introduce ourselves.
+    // Construct join message
+    int local_len = strlen(localhub);
+    int remote_len = strlen(remotehub);
+    int msg_len = local_len + remote_len + 2;
+    msg_buf[0] = msg_len;
+    msg_buf[1] = 0;
+    msg_buf[2] = 'j';
+    msg_buf[3] = remote_len;
+    memcpy(msg_buf + 4, remotehub, remote_len);
+    msg_buf[4 + remote_len] = local_len;
+    memcpy(msg_buf + 5 + remote_len, localhub, local_len);
+    msg_len += 3;       // Size word and type byte
+
+    // Send join message
+    if (msg_len != client.write(msg_buf, msg_len)) {
+        client.stop();
+        return;
+    }
+    Serial.write("Connected as ");
+    Serial.write(localhub);
+    Serial.write(" to ");
+    Serial.write(remotehub);
+    Serial.write('\n');
+}
+
+void setRGBLED(uint8_t mask, uint8_t state) {
+    uint8_t rgbledmask_old = rgbledmask;
+    if (state) {
+        rgbledmask |= mask;
+    } else {
+        rgbledmask &= (~mask);
+    }
+    if (rgbledmask_old == 0 && rgbledmask != 0) {
+        RGB.control(true);
+    }
+    if (rgbledmask_old != 0 && rgbledmask == 0) {
+        RGB.control(false);
+    }
+    if (rgbledmask != 0) {
+        RGB.color(
+            (rgbledmask & ledr)?255:0,
+            (rgbledmask & ledg)?255:0,
+            (rgbledmask & ledb)?255:0
+        );
+    }
+    return;
+}
+
+void sparkProcessCommand(void) {
+    int i;
+
+    if (((msg_buf[16] << 8) | msg_buf[15]) < 3) {
+        return;                         // Too short to hold 3-byte Cn2BASIC header
+    }
+    if (msg_buf[17] != 0x04) {          // tString
+        return;                         // Invalid header byte
+    }
+    int str_len = (msg_buf[19] << 8) | msg_buf[18];
+#ifdef sparkdebug
+    RGB.control(false);
+    Serial.print("Processing command with length ");
+    Serial.println(str_len, HEX);
+    Serial.flush();
+#endif
+    if (str_len < 0 || str_len > 252) {
+        return;                         // Invalid length
+    }
+#ifdef sparkdebug
+    Serial.print("Processing command type ");
+    Serial.println(msg_buf[20], HEX);
+    Serial.flush();
+#endif
+    switch(msg_buf[20]) {               // Type of message
+        case 'C': {
+            // Swap sender and recipient addresses
+            for(int i = 0; i < 5; i++) {
+                sender[i] = msg_buf[5 + i];
+                recipient[i] = msg_buf[10 + i];
+            }
+            str_len = 0;
+            data[0] = 0x04;     // tString
+
+            // Store local hub name into the string
+            int llen = strlen(localhub);
+            data[3] = llen;
+            str_len++;
+            for(int i=0; i < llen; i++) {
+                if (localhub[i] >= 'a' && localhub[i] <= 'z') {
+                    // Translate lowercase letter
+                    data[3 + str_len++] = 0xbb;
+                    data[3 + str_len++] = 0xb0+(localhub[i]-'a')+(localhub[i]>='l');
+                } else {
+                    data[3 + str_len++] = localhub[i];
+                }
+            }
+            // Store remote hub name into the string
+            int rlen = strlen(remotehub);
+            data[3 + str_len] = rlen;
+            str_len++;
+            for(int i=0; i < rlen; i++) {
+                if (remotehub[i] >= 'a' && remotehub[i] <= 'z') {
+                    // Translate lowercase letter
+                    data[3 + str_len++] = 0xbb;
+                    data[3 + str_len++] = 0xb0+(remotehub[i]-'a')+(remotehub[i]>='l');
+                } else {
+                    data[3 + str_len++] = remotehub[i];
+                }
+            }
+
+            data[1] = (str_len & 0x0ff);
+            data[2] = (str_len >> 8);
+            str_len += 3;
+            datasizeptr[0] = (str_len & 0x0ff);
+            datasizeptr[1] = (str_len >> 8);
+
+            int retries = 0, rval;
+            do {
+                setRGBLED(ledr, HIGH);
+                rval = cn2_send();
+                if (rval != 0) {
+                    setRGBLED(ledr, LOW);
+                    if (rval == -99) setRGBLED(ledg, HIGH);
+                    delay(100);
+                    retries++;
+                }
+            } while (rval != 0 && retries < CN2_MAX_FRAME_RETRIES);
+            return;
+        }
+            break;
+
+        case 'W': {                     // WiFi credentials
+            int type, SSIDlen, PWDlen, base;
+            char ssid[64];
+            char pwd[64];
+            type = msg_buf[21];
+            SSIDlen = msg_buf[22];
+
+            int offset = 23;
+            int j = 0;
+#ifdef sparkdebug
+            Serial.print("Copying SSID, length ");
+            Serial.println(SSIDlen);
+            Serial.flush();
+#endif
+            for(base = offset; j < SSIDlen; ) {
+                int val = msg_buf[offset++];
+                if (val == 0xbb) {
+                    val = 'a' + (msg_buf[offset++] - 0xb0);
+                    val -= (val > 'k');
+                }
+                ssid[j++] = val;
+            }
+            ssid[j] = '\0';
+            PWDlen = msg_buf[offset++];
+#ifdef sparkdebug
+            Serial.print("Copying pass, length ");
+            Serial.println(PWDlen);
+            Serial.flush();
+#endif
+            for(j = 0, base = offset; j < PWDlen;) {
+                int val = msg_buf[offset++];
+                if (val == 0xbb) {
+                    val = 'a' + (msg_buf[offset++] - 0xb0);
+                    val -= (val > 'k');
+                }
+                pwd[j++] = val;
+            }
+            pwd[j] = '\0';
+
+#ifdef sparkdebug
+            Serial.print("Setting new credentials: SSID = '");
+            Serial.print(ssid);
+            Serial.print("', pass='");
+            Serial.print(pwd);
+            Serial.print("', type=");
+            Serial.println(type);
+            Serial.flush();
+#endif
+            if (!WiFi.clearCredentials()) {
+#ifdef sparkdebug
+                Serial.println("Failed to clear existing credentials!");
+#endif
+                return;
+            }
+            switch(type) {
+                case 0:     // Open
+                    WiFi.setCredentials(ssid);
+                    break;
+                case 1:     // WEP
+                    WiFi.setCredentials(ssid, pwd, WEP);
+                    break;
+                case 2:     // WPA
+                    WiFi.setCredentials(ssid, pwd, WPA);
+                    break;
+                case 3:
+                    WiFi.setCredentials(ssid, pwd, WPA2);
+                    break;
+                default:
+                    return;     // Invalid
+            }
+#ifdef sparkdebug
+            if (WiFi.hasCredentials()) {
+                Serial.println("HasCredentials() reported true after set.");
+                Serial.flush();
+            }
+            Serial.print("New credentials saved.");
+            Serial.flush();
+#endif
+        }
+            break;
+        case 'L': {                     // Local hub name
+            int j = 0;
+            for(int i = 0; i < (str_len - 1); ) {
+                int val = msg_buf[21 + i++];
+                if (val == 0xbb) {
+                    val = 'a' + (msg_buf[21 + i++] - 0xb0);
+                    val -= (val > 'k');
+                }
+                EEPROM.write(EEPROM_LOCALHUB_OFFSET + j++, val);
+            }
+            EEPROM.write(EEPROM_LOCALHUB_OFFSET + j++, 0);            // Null terminator
+            EEPROM.write(EEPROM_MAGIC_OFFSETL, EEPROM_MAGIC);
+            copyHubsToRAM();
+        }
+            break;
+        case 'R': {                     // Remote hub name
+            int j = 0;
+            for(int i = 0; i < (str_len - 1); ) {
+                int val = msg_buf[21 + i++];
+                if (val == 0xbb) {
+                    val = 'a' + (msg_buf[21 + i++] - 0xb0);
+                    val -= (val > 'k');
+                }
+                EEPROM.write(EEPROM_REMOTEHUB_OFFSET + j++, val);
+            }
+            EEPROM.write(EEPROM_REMOTEHUB_OFFSET + j++, 0);            // Null terminator
+            EEPROM.write(EEPROM_MAGIC_OFFSETR, EEPROM_MAGIC);
+            copyHubsToRAM();
+        }
+            break;
+        default:
+            return;                     // Nothing doing
+    }
+    if (client.connected()) {
+        client.stop();
+    }
+    Spark.sleep(SLEEP_MODE_DEEP, 1);    // Reboot the Spark Core after one second
+}
+
+void copyHubsToRAM(void) {
+    for(int i=0; i < 17; i++) {
+        localhub[i] = EEPROM.read(EEPROM_LOCALHUB_OFFSET + i);
+    }
+    for(int i=0; i < 17; i++) {
+        remotehub[i] = EEPROM.read(EEPROM_REMOTEHUB_OFFSET + i);
+    }
+    return;
+}
+#endif


Working right now

Working right now

--- Goggles.ino
+++ Goggles.ino
@@ -0,0 +1,52 @@
+// Low power NeoPixel goggles example.  Makes a nice blinky display
+// with just a few LEDs on at any time.
+
+#include <Adafruit_NeoPixel.h>
+
+#define PIN 16
+#define BUTTON 9
+
+Adafruit_NeoPixel pixels = Adafruit_NeoPixel(32, PIN);
+
+uint8_t  mode   = 0, // Current animation effect
+         offset = 0; // Position of spinny eyes
+uint32_t color  = 0xFF0000; // Start red
+uint32_t prevTime;
+
+void setup() {
+  pixels.begin();
+  pixels.setBrightness(85); // 1/3 brightness
+  prevTime = millis();
+}
+
+void loop() {
+  uint8_t  i;
+  uint32_t t;
+
+  switch(mode) {
+
+   case 0:
+    for(i=0; i<32; i++) {
+      uint32_t c = 0;
+      if(((offset + i) & 7) < 2) c = color; // 4 pixels on...
+      pixels.setPixelColor(   i, c); // First eye
+     // pixels.setPixelColor(31-i, c); // Second eye (flipped)
+    }
+    pixels.show();
+    offset++;
+    delay(50);
+    break;
+  }
+
+  t = millis();
+  if((t - prevTime) > 2000) {      // Every 8 seconds...
+    mode++;                        // Next mode
+    if(mode > 0) {                 // End of modes?
+      mode = 0;                    // Start modes over
+      color >>= 8;                 // Next color R->G->B
+      if(!color) color = 0xFF0000; // Reset to red
+    }
+    for(i=0; i<32; i++) pixels.setPixelColor(i, 0);
+    prevTime = t;
+  }
+}


Create Output_Board.ino

Create Output_Board.ino

Premiere essai du output_board avec un Keystudio W5500 Board (un arduino Ethernet)

error: pathspec 'Arduino/Ethernet/Output_Board.ino' did not match any file(s) known to git

--- Arduino/Ethernet/Output_Board.ino
+++ Arduino/Ethernet/Output_Board.ino
@@ -0,0 +1,270 @@
+/*
+ * Control 8 relay outputs using buttons and MQTT messages. Subscribes to an MQTT broker and
+ * awaits commands.
+ *
+
+ * MQTT command format is "output,state". Output 0 is all outputs. Eg:
+ * 1,0 (output 1 off)
+ * 3,1 (output 3 on)
+ * 0,0 (all outputs off)
+ * 0,1 (all outputs on)
+ */
+
+//#include "config.h"
+/*--------------------------- Configuration ------------------------------*/
+/* Network config */
+#define ENABLE_DHCP                 true   // true/false
+#define ENABLE_MAC_ADDRESS_ROM      true   // true/false
+static uint8_t mac[] = { 0x00, 0x, 0xBE, 0xEF, 0xFE, 0xED };  // Set if no MAC
+static uint8_t ip[] = { 192, 168, 1, 35 }; // Use if DHCP disabled
+#define MQTTBROKER          "IPAddress"
+
+
+#include <PubSubClient.h>  // MQTT client
+
+// General setup
+uint64_t chipid;                 // Global to store the unique chip ID of this device
+
+
+// MQTT setup
+const char* mqtt_broker = MQTTBROKER;     // Configure this in "config.h"
+char msg[75];              // General purpose  buffer for MQTT messages
+char command_topic[50];    // Dynamically generated using the unique chip ID
+
+// Define pins
+int output_pin[8] =   {14, 27, 26, 25, 19, 17, 18, 23};
+int output_state[8] = { 0,  0,  0,  0,  0,  0,  0,  0};
+const int number_of_output = lenghof(output_pin);
+
+
+//PubSubClient client(espClient);
+
+void setup() {
+
+  Serial.begin(115200);
+  Serial.println();
+  Serial.println("=====================================");
+  Serial.println("Starting OutputBoard v1.0");
+
+
+  // Set up the topics for MQTT. By inserting the unique ID, the result is
+  // of the form: "device/dab9616f/command"
+  sprintf(command_topic, "device/%08x/command",  chipid);  // For receiving messages
+
+  // Report the topics to the serial console and OLED
+  Serial.print("Command topic:       ");
+  Serial.println(command_topic);
+
+  // Prepare for OTA updates
+  setup_ota();
+
+  // Set up the MQTT client
+  client.setServer(mqtt_broker, 1883);
+  client.setCallback(mqtt_callback);
+
+  // Make sure all the I/O pins are set up the way we need them
+  enable_and_reset_all_outputs();
+
+//display Ip address
+  for (byte thisByte = 0; thisByte < 4; thisByte++) {
+    // print the value of each byte of the IP address:
+    display.drawString(Ethernet.localIP()[thisByte], DEC);
+    if( thisByte < 3 )
+    {
+      box.print(".");
+    }
+  }
+}
+
+
+/*
+ * Set all the outputs to low
+ */
+void enable_and_reset_all_outputs()
+{
+  for(int i = 0; i < number_of_output; i++)
+  {
+    pinMode(output_pin[i], OUTPUT);
+    digitalWrite(output_pin[i], output_state[i]);
+    output_state[i] = 0;
+  }
+}
+
+
+/*
+ * Callback invoked when an MQTT message is received.
+ */
+void mqtt_callback(char* topic, byte* payload, unsigned int length) {
+  Serial.print("Message arrived [");
+  Serial.print(topic);
+  Serial.print("] ");
+  for (int i = 0; i < length; i++) {
+    Serial.print((char)payload[i]);
+  }
+  Serial.println();
+
+  byte output_number = payload[0] - '0';
+  byte output_state = payload[2] - '0';
+  Serial.print("Output: ");
+  Serial.println(output_number);
+  Serial.print("State: ");
+  Serial.println(output_state);
+
+  switch(output_state)
+  {
+    case 0:
+      turn_output_off(output_number);
+      break;
+    case 1:
+      turn_output_on(output_number);
+      break;
+  }
+}
+
+/*
+ * Set one or all outputs to off
+ */
+void turn_output_off( int output_number )
+{
+  char message[25];
+  byte output_index = output_number - 1;
+
+  if(output_number == 0)
+  {
+    for(int i=0; i<number_of_output; i++)
+    {
+      digitalWrite(output_pin[i], LOW);
+      output_state[i] = 0;
+    }
+    sprintf(message, "Turning OFF all outputs");
+  } else if(output_number < number_of_output) {
+    digitalWrite(output_pin[output_index], LOW);
+    output_state[output_index] = 0;
+    sprintf(message, "Turning OFF output %d", output_number);
+  }
+
+  Serial.println(message);
+}
+
+/*
+ * Set one or all outputs to on
+ */
+void turn_output_on( int output_number )
+{
+  char message[25];
+  byte output_index = output_number - 1;
+
+  if(output_number == 0)
+  {
+    for(int i=0; i<number_of_output; i++)
+    {
+      digitalWrite(output_pin[i], HIGH);
+      output_state[output_index] = 1;
+    }
+    sprintf(message, "Turning ON all outputs");
+  } else if(output_number < number_of_output) {
+    digitalWrite(output_pin[output_index], HIGH);
+    output_state[output_index] = 1;
+    sprintf(message, "Turning ON output %d", output_number);
+  }
+
+  Serial.println(message);
+}
+
+/*
+ * Repeatedly attempt connection to MQTT broker until we succeed. Or until the heat death of
+ * the universe, whichever comes first
+ */
+void reconnect() {
+
+  char mqtt_client_id[20];
+  sprintf(mqtt_client_id, "esp32-%08x", chipid);
+
+  // Loop until we're reconnected
+  while (!client.connected()) {
+    Serial.print("Attempting MQTT connection...");
+    // Attempt to connect
+    if (client.connect(mqtt_client_id)) {
+      Serial.println("connected");
+      // Once connected, publish an announcement...
+      sprintf(msg, "Device %s starting up", mqtt_client_id);
+      client.publish("events", msg);
+      // ... and resubscribe
+      client.subscribe(command_topic);
+      Serial.print("Subscribing to ");
+      Serial.println(command_topic);
+    } else {
+      Serial.print("failed, rc=");
+      Serial.print(client.state());
+      Serial.println(" try again in 5 seconds");
+      // Wait 5 seconds before retrying
+      delay(5000);
+    }
+  }
+}
+
+/*
+ * OTA updates from the Arduino IDE
+ */
+void setup_ota()
+{
+  // Port defaults to 3232
+  // ArduinoOTA.setPort(3232);
+
+  // Hostname defaults to esp3232-[MAC]
+  // ArduinoOTA.setHostname("myesp32");
+
+  // No authentication by default
+  // ArduinoOTA.setPassword("admin");
+
+  // Password can be set with its md5 value as well
+  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
+  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
+
+  ArduinoOTA
+    .onStart([]() {
+      String type;
+      if (ArduinoOTA.getCommand() == U_FLASH)
+        type = "sketch";
+      else // U_SPIFFS
+        type = "filesystem";
+
+      // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
+      Serial.println("Start updating " + type);
+    })
+    .onEnd([]() {
+      Serial.println("\nEnd");
+    })
+    .onProgress([](unsigned int progress, unsigned int total) {
+      Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
+    })
+    .onError([](ota_error_t error) {
+      Serial.printf("Error[%u]: ", error);
+      if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
+      else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
+      else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
+      else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
+      else if (error == OTA_END_ERROR) Serial.println("End Failed");
+    });
+
+  ArduinoOTA.begin();
+}
+
+
+/*
+ * =========================================================================
+ * Main program loop
+ */
+void loop() {
+  // Handle OTA update requests
+  ArduinoOTA.handle();
+
+  // Always try to keep the connection to the MQTT broker alive
+  if (!client.connected()) {
+    reconnect();
+  }
+
+  // Handle any MQTT tasks
+  client.loop();
+
+}


Initial watermeter pulse indicator - code

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/HouseOfTheFuture/IoT-Device.git/'