Any changes made to development imps today?

Were there any changes to development imps or their environment today? I’ve had some imp trouble and it seemed to coincide with a fair amount of lag in the ide.

I have several imps that went offline and cannot reconnect, even after power cycle. Nothing changed in the code, they’ve been running fine for months.
One example : impeeID: 20000c2a690524ee

Additionally, is there a stack limit in the agent? I’ve been having some serious trouble since yesterday with code that I thought was stable. I have code that goes probably somewhere between 10 and 20 nested calls deep. I’ve narrowed that problem to my own json encoder that I’ve been using for months (on both agent and device) without a problem. It does use recursion but is otherwise rock-solid.

We experienced some issues earlier that today that should now be resolved:

http://forums.electricimp.com/discussion/2658/device-running-very-slow#Item_15

If you suspect it’s your JSON encoder you might want to add imp.getmemoryfree() to the start of the function to see how memory usage drops (or doesn’t).

Out of curiosity, is there a reason you’re not just using http.jsonencode

Yes, thanks for asking!!!
I need serialisation on the device side. http.jsonencode isn’t available, so I use my own. It’s handy to output structures on the debug port of a device when the device is offline. I also use it to serialise settings that need to be preserved in the device over power-cycles. I know ElectricImp use a serialisation scheme for device.send and agent.send. It would be great if the encoding/decoding for that were exposed for use in the device, so I can reclaim some valuable device code space. :slight_smile:

BTW, it’s not my JSON encoder that seems to be the problem alone. When I am very deeply nested in the agent and invoke my json encoder within a call to server.log, some of my blobs (which I encode as printable hexstrings) magically turn into functions after server.log returns. I can’t quite explain the behaviour. It may very well be a bug that I’ve introduced, but I’m shocked to see it happen in code of mine that I thought I had road-tested. This was the reason for my question about the stack. Does Squirrel prevent a stack-blowout by returning a function instead of invoking it?

I’m assuming you’re storing the data in some SPI flash?

I don’t think Squirrel prevents a stack-blowout by returning a function instead of invoking it (and I certainly have never seen that).

If you care to share some code i would be happy to look :slight_smile:

Squirrel does support tail recursion, see:

http://www.squirrel-lang.org/doc/squirrel3.html#d0e1569

…I think this is what you mean? If you have code that misbehaves we’d love to see it.

I’m using general recursion rather than tail recursion for the encoder and decoder, so there would definitely be some impact on the stack. However, it’s worked quite happily for me when encoding the results of imp.scanwifinetworks() and other larger structures. I’m mindful of how I use them in the device because I assume it’s far more limited in resources. In the agent, I’ve been less thrifty. That said, the particular problem I encountered involved json-encoding (in the agent) of a shallow 8 element table. But, I’d happily accept advice on how I could make this encoder less stack hungry.

`/**************************** JSON Encode *************************************/
// pass any structure to encode()

// given a blob of data, or a string, returns string of hexadecimal values
function toHexStr(b){
local length = b.len();
local result = blob(length*2);
for (local i = 0; i < length; i+=1) {
local ch = b[i]>>>4;
ch += (ch>9)? ‘7’:‘0’;
result.writen(ch,‘b’);
ch = b[i] & 0x0f;
ch += (ch>9)? ‘7’:‘0’;
result.writen(ch,‘b’);
}
return result.tostring();
}

// quickly scan string for any unsafe characters
function isSafe(str){
    // anything < ' ' or > '~'
    // " or \\ or /
    foreach (ch in str) {
        switch(ch) {
            case '"':
            case '/':
            case '\\\\':
                return false;
            default:
                if (ch < ' ' || ch > '~')
                    return false;
        }
    }
    return true;
}

function charHi(ch){
    local hi = ch>>>4;
    hi += (hi>9)? '7':'0';
    return hi;
}

function charLo(ch){
    local lo = ch & 0x0f;
    lo += (lo>9)? '7':'0';
    return lo;
}

// converts string to a safe format for serialisation
function convertToSafe(str){
    local safe = "";
    
    local JSON_ESC_ENCODES = {
		['\\b'] = 'b',
		['\\t'] = 't',	
		['\

‘] = ‘n’,
[’\f’] = ‘f’,
[’\r’] = ‘r’,
[’"’] = ‘"’,
[’\\’] = ‘\\’,
[’/’] = ‘/’
}

    foreach (ch in str) {
        if (ch in JSON_ESC_ENCODES)
            safe += "\\\" + JSON_ESC_ENCODES[ch].tochar();
        else if (ch < ' ' || ch > '~')
                safe += "\\\\x" + charHi(ch).tochar() + charLo(ch).tochar();
            else safe += ch.tochar();
    }
    return safe;
}

function encode(obj){
    switch(typeof(obj)) {
        
        case "string":
            if (isSafe(obj))
                return "\"" + obj + "\"";
            else {
                server.log("JSON Unsafe String Detected: ("+obj.len()+")");
                return "\"" + convertToSafe(obj) + "\"";
            }

        case "table":
            local tstr = "{";
            local j = 0;
            foreach( key, value in obj){
                if (isSafe(key)) {
                    tstr += "\"" + key + "\": " + encode(value);
                    if (++j<obj.len())
                        tstr += ", ";
                }
                else
                    // ERROR, key name must be safe
                    throw "JSON keys must be SAFE";
            }
            return tstr + "}";

        case "bool":
        case "float":
        case "integer":
            return obj;
            
        case "null":
        case "function":
            return "null";

        case "array":        
            local astr = "[";
            local k = 0;
            foreach(element in obj){
                astr += encode(element);
                if (++k<obj.len())
                    astr += ", ";
            }
            return astr + "]";
            
        case "blob":
            return "\"" + toHexStr(obj) + "\"";

}
}`