Imp, Camera, Action!

Sorry to add to this again, still trying to nut it out. What does “stream” mean in this context? I tried directly reading from spi to agent without storing an intermediate local variable:

local len = myCamera.read_fifo_length();
....
agent.send("img",spi.readblob(len));

But the agent.send line still causes the out-of-memory error when len is > 65528. Is there another way to “stream”?

Yes - essentially, you read the image out in chunks and send each chunk separately.

Here’s a snippet…

Device side:

// size of data chunks to send agent. Large multiple of 8.
static CHUNK_SIZE = 8192;

// next chunk to send
chunk_next = 0;

// number of chunks to send
chunk_count = 0;

function send_chunk() {
    agent.send("jpeg_chunk", [chunk_next*CHUNK_SIZE, spi.readblob(CHUNK_SIZE)]);

    // All done?
    chunk_next++;
    if (chunk_next < chunk_count) imp.wakeup(0, send_chunk.bindenv(this));
    else {
        cs_l.write(1); 
        agent.send("jpeg_end",1);
    }
}

function send_buffer() {
    local len = read_fifo_length();
    chunk_count = math.ceil(len.tofloat()/CHUNK_SIZE).tointeger();
    
    server.log(format("%d bytes: %d chunks",len,chunk_count));
    cs_l.write(0);
    set_fifo_burst();
    spi.readblob(1); //dummy read
    
    // Send header
    agent.send("jpeg_start", len);
    
    // As buffer can be big, we do the sending on imp.wakeup(0) to allow
    // incoming messages to be processed
    chunk_next = 0;
    imp.wakeup(0, send_chunk.bindenv(this));
}

Agent side:

jpeg_buffer <- null
jpeg_startat <- 0;
image <- null

device.on("jpeg_start", function(size) {
    jpeg_buffer = blob(size);
    jpeg_startat = time();
});

device.on("jpeg_chunk", function(v) {
    // check we've not got some barf from a previous boot
    if (jpeg_buffer == null) return;

    local offset = v[0];
    local b = v[1];
    for(local i = offset; i < (offset+b.len()); i++) {
        if(i < jpeg_buffer.len()) {
            jpeg_buffer[i] = b[i-offset];
        }
    }
});

device.on("jpeg_end", function(v) {
    // check we've not got some barf from a previous boot
    if (jpeg_buffer == null) return;

    // copy last JPEG to web server blob
    image = jpeg_buffer

    server.log(format("Agent: JPEG Received (%d bytes) at rate of %.2fkB/s",image.len(), (image.len()/1024.0)/(time()-jpeg_startat)));
    server.log(format("Agent memory remaining: %d bytes", imp.getmemoryfree()));
});

So, you call send_buffer() and it reads the length, rounds it up to CHUNK_SIZE (I had this as 8192 bytes), and sends a “jpeg_start” message to the agent with the length.

The agent then allocates a blob big enough to hold the entire image, and waits for “jpeg_chunk” messages, filling the blob in as it goes. When it gets a “jpeg_end” the whole image has been received (and in this case is served from the agent directly onto a webpage).

There will be some assembly required, but you should get the idea from this…

Works nicely, thanks @hugo