A Quick and Dirty Guide to Getting Started with the Pinoccio API.

In case I haven't told you, Pinoccio is an Ardunio-esque micro-controller board, that ships 'with wings': Each board has built-in mesh networking, and the kit comes with a Wifi board.

What's particularly unique about Pinoccio is that they're not leaving it just at that. They're providing a simple scripting language (based on the phenomenally cool Bitlash project), as well as an associated web-service and API provider (called HQ).

In Pinoccio parlance, a single board is called scout and a group of scouts connected by a mesh are called a troop. Cute eh?

Today, in the spirit of taking time off from thesis writing, and getting my head away from Transactional Memory, I figured I'd spend some time digging into the API they provide. I thought I'd write up some example code here, from my perspective (a relative novice to web-services entirely).

We're going to create two scripts: issue_command.py and sync_stream.py. The first will allow issuing a scout-script command through the API, and the second, will allow you to watch the API's sync stream, which watches all the events which hit HQ.

Before those though, I'll show you api_explorer.py which will allow exploring the read only elements of the API.

Authentication:

First step is to login and get an authenticated token, which is a string the API gives you as a temporary password which you attach to all your subsequent requests. This ensures that you, and only you, can control your Pinoccio boards.

For the purposes of this blog post, I'm going to skip the right way to do this, in order to get up and running faster, by showing you the dirty way, straight from the Pinoccio API Documentation.

On the command line, you can ask the API for a token using curl:

curl -X POST -v --data '{"email":"youremail","password":"boats"}' https://api.pinocc.io/v1/login

Take note of the reply:

{"data":{"token":"ahv0onrd0fiio8813llqjo5sb1","account":19}}

You'll need the token shortly.

HTTP Requests

I've been quite impressed with the python library Requests, which makes it reasonably easy to communicate with a service, bidirectionally.

You can install it, if you don't already have it, with pip:

pip install requests

api_explorer.py

While you could do this directly with curl, it's instructive to see how requests are constructed before going on further. To that end: here's api_explorer.py

import requests
import sys

API_Root="https://api.pinocc.io/v1/"
token = "your token here!"
auth_arg = {"token" : token} 

def get(url, **kwargs): 
    """
    Issue a get request to the Pinnocio API, authenticated by the token
    """
    if "data" in kwargs: 
        kwargs["data"] = dict(kwargs["data"].items() + auth_arg.items())
    else:
        kwargs["data"] = auth_arg
    return requests.get(API_Root + url, **kwargs)


if __name__  == '__main__': 
    if len(sys.argv) > 1: 
        r = get(sys.argv[1])
        print r.status_code
        print r.text
    else:
        print "Need a command" 

This simple script allows you to provide an endpoint on the command line, and print out the API's response. The function get above constructs the appropriate URL given the endpoint name, and makes sure that we have the token on the request. The kwargs is so that we can provide arguments to the call, which we will need soon...

troops is an interesting endpoint, as it will list the troops associated with the account you used to generate the token.

SolidALumbook:matt_pinoccio mgaudet$ python api_explore.py troops
200
{"data":[{"account":1112,"token":"<token data-preserve-html-node="true" elided>","name":"Scooters","id":1,"online":true}]}

Reading this JSON output, you can see I have one troop, "Scooters", with id 1. So, we can now ask the API about the scouts in that troop:

SolidALumbook:matt_pinoccio mgaudet$ python api_explore.py 1/scouts
200
{"data":[{"name":"Razer","updated":"1397008385993","id":1,"time":1397008385992},{"name":"Rascal","updated":"1397008855488","id":2,"time":1397008855488}]}

So I have two scouts in the troop: Razer (id 1) and Rascal (id 2).

These ID's are useful when talking about issuing commands.

issue_command.py

According to the API documents, to issue a ScoutScript command to a scout, you need to know its troop id and scout id. After which, you can issue a command with HTTP GET on the end point {troop id}/{scout id}/command. Using api_explorer.py we found out that Razer is scout 1 in troop 1. So if we want to issue a command to Razer, we can make a request to the 1/1/command endpoint.

Here's a script which allows you to specify a troop id, scout id, and a ScoutScript command. As you can see it's largely identical to api_explorer.py, except that "command" is provided as an argument to the get-request.

import requests
import sys

API_Root="https://api.pinocc.io/v1/"
token = "your token here"
auth_arg = {"token" : token} 

def get_token(username, password): 
    pass

def get(url, **kwargs): 
    if "data" in kwargs: 
        kwargs["data"] = dict(kwargs["data"].items() + auth_arg.items())
    else:
        kwargs["data"] = auth_arg
    return requests.get(API_Root + url, **kwargs)

if __name__  == '__main__': 
    if len(sys.argv) > 3: 
        command = " ".join(sys.argv[3:])
        r = get("{}/{}/command/".format(sys.argv[1],sys.argv[2]),data={"command": command}) 
        print r.status_code
        print r.text
    else:
        print "Need a troop, a scout, and a command" 

So we can turn on the led of Razer:

SolidALumbook:matt_pinoccio mgaudet$ python issue_command.py 1 1 led.on
200
{"data":{"type":"reply","from":1,"id":"t44947","end":true,"reply":"","_cid":13,"basetime":469,"commandsPending":0,"messagesQueued":0,"account":"1112","tid":"1","_t":1397421971331,"output":"","result":false}}

Or peek at the wifi status:

SolidALumbook:matt_pinoccio mgaudet$ python issue_command.py 1 1 "wifi.report()"
200
{"data":{"type":"reply","from":1,"id":"t44948","end":true,"reply":"{\"type\":\"wifi\",\"connected\":true,\"hq\":true}\r\n","_cid":14,"basetime":400,"commandsPending":0,"messagesQueued":0,"account":"1112","tid":"1","_t":1397421998605,"output":"{\"type\":\"wifi\",\"connected\":true,\"hq\":true}\r\n","result":false}}

sync_stream.py

One of the interesting elements of the Pinnocio API is that the scouts themselves will autonomously report information back to HQ. This data can be accessed through the API from the sync endpoint.

Here's a simple script which starts listening to the sync stream until killed:

import requests
import sys
import json

API_Root="https://api.pinocc.io/v1/"
token = "your API key here!"
auth_arg = {"token" : token} 

def get_token(username, password): 
    pass

def get(url, **kwargs): 
    if "data" in kwargs: 
        kwargs["data"] = dict(kwargs["data"].items() + auth_arg.items())
    else:
        kwargs["data"] = auth_arg
    return requests.get(API_Root + url, **kwargs)

if __name__  == '__main__': 
    received = 0
    r = get("sync",stream=True)
    print r.status_code

    # Chunk size required to avoid buffering output too long!
    for line in r.iter_lines(chunk_size=1): 
        received = received + 1 
        if line: 
            print "({}) {}".format(received, json.loads(line))
        else:
            print "({}) keepalive".format(received) 

Here's what it looks like in action:

SolidALumbook:matt_pinoccio mgaudet$ python sync_stream.py 
200
(1) {u'data': {u'account': u'1112', u'type': u'connection', u'troop': u'1', u'value': {u'status': u'online', u'ip': u'<ip data-preserve-html-node="true" elided>'}, u'time': 1397420589928}}
(2) {u'data': {u'account': u'1112', u'value': {u'state': [-1, -1, -1, -1, -1, -1, -1, -1], u'_t': 1397420590333.001, u'type': u'analog', u'mode': [-1, -1, -1, -1, -1, -1, -1, -1]}, u'scout': u'1', u'troop': u'1', u'time': 1397420590333.001, u'type': u'analog'}}
(3) {u'data': {u'account': u'1112', u'value': {u'available': 1, u'_t': 1397420590149.001, u'scout': 1, u'type': u'available'}, u'scout': u'1', u'troop': u'1', u'time': 1397420590149.001, u'type': u'available'}}
(4) {u'data': {u'account': u'1112', u'value': {u'available': 1, u'_t': 1397421532918.001, u'scout': 2, u'type': u'available'}, u'scout': u'2', u'troop': u'1', u'time': 1397421532918.001, u'type': u'available'}}
(5) {u'data': {u'account': u'1112', u'value': {u'list': [], u'_t': 1397420590332.001, u'type': u'backpacks'}, u'scout': u'1', u'troop': u'1', u'time': 1397420590332.001, u'type': u'backpacks'}}
(6) {u'data': {u'account': u'1112', u'value': {u'state': [-1, -1, -1, -1, -1, -1, -1], u'_t': 1397420590333, u'type': u'digital', u'mode': [-1, -1, -1, -1, -2, -2, -2]}, u'scout': u'1', u'troop': u'1', u'time': 1397420590333, u'type': u'digital'}}
(7) {u'data': {u'account': u'1112', u'value': {u'led': [0, 255, 0], u'torch': [0, 255, 0], u'type': u'led', u'_t': 1397421971069}, u'scout': u'1', u'troop': u'1', u'time': 1397421971069, u'type': u'led'}}
(8) {u'data': {u'account': u'1112', u'value': {u'led': [0, 34, 255], u'torch': [0, 34, 255], u'type': u'led', u'_t': 1397421537370}, u'scout': u'2', u'troop': u'1', u'time': 1397421537370, u'type': u'led'}}
(9) {u'data': {u'account': u'1112', u'value': {u'scoutid': 1, u'power': u'3.5 dBm', u'troopid': 1, u'_t': 1397420590334, u'rate': u'250 kb/s', u'routes': 0, u'type': u'mesh', u'channel': 20}, u'scout': u'1', u'troop': u'1', u'time': 1397420590334, u'type': u'mesh'}}
(10) {u'data': {u'account': u'1112', u'value': {u'vcc': True, u'battery': 73, u'_t': 1397422079321, u'voltage': 392, u'type': u'power', u'charging': False}, u'scout': u'1', u'troop': u'1', u'time': 1397422079321, u'type': u'power'}}

Download code:

All this code has been uploaded to github here, as PinoccioApiTools.

Conclusion

Pinoccio is providing some very powerful tools for building some interesting projects, minimizing a lot of pain points that previous solutions suffered from. I'm really excited by this.

What Caught My Eye - Week 12

Technology & Tech Culture:

  • This video featuring Monty Montgomery from Xiph is a phenomenal and cogent explanation of how analog to digital to analog conversion works, and why the stair-step chart we often see is a horrible lie.
  • Hiring is a bit of voodoo, and often times I hear people talk about Passion, which is a bit of a bugbear of mine. This post talks about how excellent candidates can be overlooked because the signifiers of 'Passion' aren't there.

Arts and Culture

Examining "Reproducibility in Computer Science"

I'm heartened to see Professor Shriram Krishnamurthi spearheading an effort to help evaluate yesterday's reproducibility project.

We are evaluating the results presented by the study at the University of Arizona. Our goal is to allow authors and any other interested parties to review that study’s findings about an individual paper, and attempt to reconstruct their findings. We will summarize the results here.
We are grateful to Collberg, et al. for initiating this discussion and making all their data available. This is a valuable service based on an enormous amount of manual labor. Even if we end up disagreeing with some of their findings, we remain deeply appreciative of their service to the community by highlighting these important issues.
We do wish disagree with the use of the term “reproducibility”, which many people associate with an independent reconstruction of the work. For instance, this paper spells out the difference between repeatability and reproducibility and provides an interesting case study.

I've already submit a dispute, where code isn't available where it was thought that it was. I plan to do a little more looking, time permitting.

Others have successfuly built projects reported as failures. I think that the community is going to help repair the results of the other study.

Combined, they're going to say much more about the state of the field!


Edited to Add: 

Looks like Dr. Krishnamurthi's effort sprung from two discussions on social media, which are worth trawling for insight: 

Source: http://cs.brown.edu/~sk/Memos/Examining-Re...

Reproducibility in CS

So, there’s a project from the University of Arizona that’s trying to investigate CS papers and see how reproducible they are.

Image from the Reproducibility in Computer Science page, retrieved&nbsp;March 19, 2014.&nbsp;

Image from the Reproducibility in Computer Science page, retrieved March 19, 2014. 

It’s a cool project. Made a pretty picture to be sure! The problem is, their pretty picture is… pretty misleading when it comes to build failures.

There’s many more like this — I took a cursory look through their results, and here’s a couple of categories:

Path setup failures:

Miscellaneous student errors:

I’m a little sad that this is what they decided to put out. Their definition of reasonable effort is

In our experiments we instructed the students to spend no more than 30 minutes on building the systems. In many cases this involved installing additional libraries and compilers, editing makefiles, etc. The students were also instructed to be liberal in their evaluations, and, if in doubt, mark systems as buildable.

Yet, as you can see above, it appears nobody actually looked at what the students said. They were taken at their word, and they were far from liberal!

Moreover, I also disagree that an undergrad given 30 minutes is a good check. Especially considering the quality of the above results.

Shaming researchers through bad methodology like this isn't going to get more people to put out code.

I love the idea of this investigation, but I don’t love the methodology.


Edited to Add:: One thing that I did love from this project was their proposal in the technical report:

Our proposal is therefore much more modest. Rather than forcing authors to make their systems available, we propose instead that every article be required to specify the level of reproducibility a reader or reviewer should expect.

This is a worthwhile baby-step in and of itself, and should probably be integrated into paper classification systems. THis would eventually provide evolutionary pressure towards reproducibility.