Hello IOT-Kit, Introduction By Example

February 12, 2018 at 01:54 AM | categories: iotkit, python, iot, C++, definitions, iotoy | View Comments

IOT-Kit is a toolkit for enabling the creation of IOT devices, by people who can make simple arduino based devices. Rather than waffle on about why, how, etc, I think the best way of explaining what this does, is by example.

Specifically, this post covers:

  • Suppose you can make little arduino based robots
  • Suppose you want to remote control your robots over the local network, trivially from something like python. (Really over HTTP in fact!)

What do you as a device maker need to do to make this happen?

IOT-Kit - Make your Arduino Device an IOT Device, easily

So the really short version is this: you can make a simple robot, but you want to make it usable as an IOT device too. You don't want to build the entire stack. You don't want to build everything yourself.

You want your users to be able to type something like this program and have it search for the robot on the network, and have it control the robot.

from iotoy.local import simplebot
import time
import random

simplebot.lights = 0
while True:
    choice = random.choice(("forward", "backward", "left", "right", "blink", "stop"))
    if choice == "forward":
        simplebot.forward()
    if choice == "backward":
        simplebot.backward()
    if choice == "left":
        simplebot.left()
    if choice == "right":
        simplebot.right()
    if choice == "blink":
        for i in range(3):
            simplebot.lights = 1
            time.sleep(0.5)
            simplebot.lights = 0
            time.sleep(0.5)
    if choice == "stop":
        simplebot.stop()

    time.sleep(5)

NOTE: While this is python, this actually maps to a bunch of deterministic http web calls, and actually can be written in any langauge. iotoy/iotkit just provides a bunch of convenience functions to do these calls in a way that also maps cleanly to python. (It also would map cleanly in javascript, ruby, perl, etc)

How do we get to this?

Building the Robot - Hardware

These is the easy part. We could use a DAGU mini-driver. This can control a number of servos and also provides serial access over plain old hardware serial bluetooth.

Building the Robot - Software, No IOT

If we were just controlling the robot without any remote control, we could use Pyxie to program this. The Pyxie program you might use could look like this:

#include <Servo.h>

leftwheel = Servo()
rightwheel = Servo()

headlights_led_pin = 13
leftwheel_pin = 2
rightwheel_pin = 3

pinMode(headlights_led_pin, OUTPUT)
leftwheel.attach(leftwheel_pin)
rightwheel.attach(rightwheel_pin)

leftwheel.write(90)
rightwheel.write(90)

while True:
    leftwheel.write(180)
    rightwheel.write(180)
    delay(500)

    leftwheel.write(180)
    rightwheel.write(0)
    delay(500)

    leftwheel.write(0)
    rightwheel.write(0)
    delay(500)

    leftwheel.write(0)
    rightwheel.write(180)
    delay(500)

    leftwheel.write(90)
    rightwheel.write(90)
    delay(500)

    digitalWrite(headlights_led_pin, HIGH)
    delay(1000)
    digitalWrite(headlights_led_pin, LOW)
    delay(1000)

This program assume 2 continuous rotation servos, where the centre point 90 means stationary, 0 means full reverse, and 180 means full forward.

What this program means is "forward, right, backward, left, stop, blink headlights".

Pyxie generates C++ code, which we can use as a starting point for our code:

#include <Servo.h>

#include "iterators.cpp"

void setup() {
    int headlights_led_pin;
    Servo leftwheel;
    int leftwheel_pin;
    Servo rightwheel;
    int rightwheel_pin;

    leftwheel = Servo();
    rightwheel = Servo();
    headlights_led_pin = 13;
    leftwheel_pin = 2;
    rightwheel_pin = 3;
    pinMode(headlights_led_pin, OUTPUT);
    (leftwheel).attach(leftwheel_pin);
    (rightwheel).attach(rightwheel_pin);
    (leftwheel).write(90);
    (rightwheel).write(90);
    while (true) {
        (leftwheel).write(180);
        (rightwheel).write(180);
        delay(500);
        (leftwheel).write(180);
        (rightwheel).write(0);
        delay(500);
        (leftwheel).write(0);
        (rightwheel).write(0);
        delay(500);
        (leftwheel).write(0);
        (rightwheel).write(180);
        delay(500);
        (leftwheel).write(90);
        (rightwheel).write(90);
        delay(500);
        digitalWrite(headlights_led_pin, HIGH);
        delay(1000);
        digitalWrite(headlights_led_pin, LOW);
        delay(1000);
    };
}

void loop() {
}

Making a simple device abstraction layer for our device.

A device abstraction layer just means creating names for the key functionality we care about. At some point, pyxie will help here, but pyxie is currently very simple and can't create functions, so we take the C++ code we have so far and work from there.

The interators in the iterators.cpp file are not used, so we can ditch that too.

Creating functions for functionality

So our first step is to pull out and name all the functions. While we're at it, unlike pyxie, we'll split out the contents of what would normally be in a setup() and loop() in an arduino program.

#include <Servo.h>

int headlights_led_pin;

Servo leftwheel;
Servo rightwheel;

int leftwheel_pin;
int rightwheel_pin;

void forward() {
    leftwheel.write(180);
    rightwheel.write(180);
    delay(500);
}
void backward() {
    leftwheel.write(0);
    rightwheel.write(0);
    delay(500);
}
void left() {
    leftwheel.write(180);
    rightwheel.write(0);
    delay(500);
}
void right() {
    leftwheel.write(0);
    rightwheel.write(180);
    delay(500);
}
void stop() {
    leftwheel.write(0);
    rightwheel.write(0);
    delay(500);
}
void lights_on() {
    digitalWrite(headlights_led_pin, HIGH);
}
void lights_off() {
    digitalWrite(headlights_led_pin, LOW);
}

void setup() {
    leftwheel = Servo();
    rightwheel = Servo();
    headlights_led_pin = 13;
    leftwheel_pin = 2;
    rightwheel_pin = 3;

    pinMode(headlights_led_pin, OUTPUT);
    leftwheel.attach(leftwheel_pin);
    rightwheel.attach(rightwheel_pin);
    leftwheel.write(90);
    rightwheel.write(90);
}

void loop() {
    forward();
    right();
    backward();
    left();

    lights_on();
    delay(1000);
    lights_off();
    delay(1000);
}

Device abstraction for our robot

So the device abstraction layer for our device has the following signature:

void forward();
void backward();
void left();
void right();
void stop();
void lights_on();
void lights_off();

This is the what we need to build an IOT-Kit interface for.

Minimal IOT-Kit Interface

Our starting point for our IOT-Kit interface is something minimal. Initially we'll try to cover the following parts of our device abstraction:

void forward();
void stop();
void lights_on();
void lights_off();

We'll then add the rest in.

Changes to support minimal control API

We add the following include near the top of the file:

#include <CommandHostTiny.h>

In order to make our device introspectable and controllable, we need to add in a class which subclasses "CommandHostTiny".

The skeleton of this class looks like this:

class SimplebotHost : public CommandHostTiny {
private:
    char temp_str[128];   // needed for parsing input
    int lights;           // To store state of the headlight
public:
    SimplebotHost() : lights(0) { }
    ~SimplebotHost() { }

    const char *hostid(); // Returns the name of the device
    const char * attrs(); // Returns the list of attributes(+types) that can be changed
    const char * funcs(); // Returns the list of functions the device understands.

    bool has_help(char * name); // To allow us to find out whether a given name has help.

    void help(char * name); // Returns the help for a given name - usually a function
                            // Includes machine parsable type signature

    bool exists(char * attribute); // Returns true/false for an attribute existing.

    const char *get(char * attribute); // Gets the value for an attribute

    int set(char* attribute, char* raw_value); // Sets the value for attributes

    int callfunc(char* funcname, char* raw_args); // Calls the given function with given raw_args
};

So by way of example, hostid, attrs and funcs in this case look like this:

const char *hostid() {    return "simplebot";     }
const char * attrs() {    return "lights:int";    }
const char * funcs() {    return "forward,stop";  }

Note that the name returned as host id here - "simplebot" - is used as the name to advertise the robot on the network, and that is how this line of python is made to work:

from iotoy.local import simplebot

Help is implemented in two ways - firstly to note that help is available and then to return the help available:

bool has_help(char * name) {
    if (strcmp(name,"forward")==0) return true;
    if (strcmp(name,"stop")==0) return true;
    return false;
}

void help(char * name) {
    if (strcmp(name,"forward")==0) Serial.println(F("forward -> - Move forward for 1/2 second"));
    else if (strcmp(name,"stop")==0) Serial.println(F("stop -> - Stop moving"));
    else Serial.println(F("-"));
}

Attribute handling is then done as follows. Note we only have one attribute - lights. ANd here I choose to update the LED state whenever the lights value changes:

bool exists(char * attribute) {
    if (strcmp(attribute,"lights")==0) return true;
    return false;
}

const char *get(char * attribute) {
    if (strcmp(attribute,"lights")==0) { 
        itoa (lights, temp_str, 10); 
        return temp_str; 
    }
    return "-";
}

int set(char* attribute, char* raw_value) {
    if (strcmp(attribute,"lights")==0) {
        int value = atoi(raw_value);
        lights = value;
        if (lights) {
            lights_on();
        } else {
            lights_off();
        }
        return 200;
    }
    return 404;
}

Handling function calls is pretty simple:

int callfunc(char* funcname, char* raw_args) { 
    if (strcmp(funcname,"forward")==0) { forward(); return 200; }
    if (strcmp(funcname,"stop")==0) { backward(); return 200; }
    return 404; 
}

IOT-kit final step

At this stage, the command host isn't being used.

Our final step in our transformation boils down to:

  • Add the other functions from our device abstraction
  • Move the setup for the robot into a setup function in the class
  • Make sure that setup also sets up the command host
  • Make the arduino set up set up our robot
  • Remove the custom code from loop() and run the command host instead.

In practice this means that our final code looks like this:

#include <Servo.h>
#include <CommandHostTiny.h>

int headlights_led_pin;

Servo leftwheel;
Servo rightwheel;

int leftwheel_pin;
int rightwheel_pin;

void forward() {
    leftwheel.write(180);
    rightwheel.write(180);
    delay(500);
}
void backward() {
    leftwheel.write(0);
    rightwheel.write(0);
    delay(500);
}
void left() {
    leftwheel.write(180);
    rightwheel.write(0);
    delay(500);
}
void right() {
    leftwheel.write(0);
    rightwheel.write(180);
    delay(500);
}
void stop() {
    leftwheel.write(0);
    rightwheel.write(0);
    delay(500);
}
void lights_on() {
    digitalWrite(headlights_led_pin, HIGH);
}
void lights_off() {
    digitalWrite(headlights_led_pin, LOW);
}

class SimplebotHost : public CommandHostTiny {
private:

    char temp_str[128];
    int lights; //

public:
    SimplebotHost() : lights(0) { }
    ~SimplebotHost() { }

    const char *hostid() {    return "simplebot";     }
    const char * attrs() {    return "lights:int";    }
    const char * funcs() {    return "forward,backward,left,right,stop";  }

    bool has_help(char * name) {
        if (strcmp(name,"forward")==0) return true;
        if (strcmp(name,"backward")==0) return true;
        if (strcmp(name,"left")==0) return true;
        if (strcmp(name,"right")==0) return true;
        if (strcmp(name,"stop")==0) return true;
        return false;
    }

    void help(char * name) {
        if (strcmp(name,"forward")==0) Serial.println(F("forward -> - Move forward for 1/2 second"));
        else if (strcmp(name,"backward")==0) Serial.println(F("backward -> - Move backward for 1/2 second"));
        else if (strcmp(name,"left")==0) Serial.println(F("left -> - Spin left for 1/2 second"));
        else if (strcmp(name,"right")==0) Serial.println(F("right -> - Spin right for 1/2 second"));
        else if (strcmp(name,"stop")==0) Serial.println(F("stop -> - Stop moving"));
        else Serial.println(F("-"));
    }

    bool exists(char * attribute) {
        if (strcmp(attribute,"lights")==0) return true;
        return false;
    }

    const char *get(char * attribute) {
        if (strcmp(attribute,"lights")==0) { 
            itoa (lights, temp_str, 10); 
            return temp_str; 
        }
        return "-";
    }

    int set(char* attribute, char* raw_value) {
        if (strcmp(attribute,"lights")==0) {
            int value = atoi(raw_value);
            lights = value;
            if (lights) {
                lights_on();
            } else {
                lights_off();
            }
            return 200;
        }
        return 404;
    }

    int callfunc(char* funcname, char* raw_args) { 
        if (strcmp(funcname,"forward")==0) { forward(); return 200; }
        if (strcmp(funcname,"backward")==0) { backward(); return 200; }
        if (strcmp(funcname,"left")==0) { left(); return 200; }
        if (strcmp(funcname,"right")==0) { right(); return 200; }
        if (strcmp(funcname,"stop")==0) { backward(); return 200; }
        return 404; 
    }

    void setup(void) {
        // Setup the pins
        CommandHostTiny::setup();

        leftwheel = Servo();
        rightwheel = Servo();
        headlights_led_pin = 13;
        leftwheel_pin = 2;
        rightwheel_pin = 3;

        leftwheel.attach(leftwheel_pin);
        rightwheel.attach(rightwheel_pin);
        leftwheel.write(90);
        rightwheel.write(90);

        pinMode(headlights_led_pin, OUTPUT);
    }
};

SimplebotHost MyCommandHost;

void setup()
{
    MyCommandHost.setup();
}

void loop() {
    MyCommandHost.run_host();
}

Final notes

So, that's a whistle stop tour of the device layer. The fun thing now: assuming this robot has a hardware serial bluetooth (ala the dagu mini), then this is everything you need to do as an arduino based maker to make your device an IOT-able device. If you're not using bluetooth, then your device assumes it's doing serial down a cable.

Either way though, as a device maker, this is all the changes you need to do to enable the python program we started with to be able to control your robot over a network.

I'll explain how this works in a later blog post, but I thought this would make a good fun first example about how IOT-Kit gets implemented by a device maker to enable a very high level of abstraction to take place.

Read and Post Comments

Escaping The Panopticon of Things?

February 11, 2018 at 10:48 PM | categories: iotkit, python, iot, C++, definitions, iotoy, opinion | View Comments

The Panopticon of Things

The Internet of Things. Ask 100 different people what it means to them and you get a 100 different answers. I know, because I have done... When you do, in my experience you some different versions and themes.

For many companies though, futurists, and techies it boils down to some variation of this:

  • People have devices, which can detect things or have small amounts of processing power added
  • These devices monitor their state or activity, or similar
  • This is published on the internet via a central service
  • This information can be aggregated and worked on, and often can be drilled into down to individual items

But is that really an internet of things? Let alone "The internet of things"? No, it's internet connected things that reports information about you, your environment or your activity to a centralised system. Some extend this to the idea of connecting these centralised systems to each other.

So no, they're not really an internet of things. They're a panopticon of things.

If you're doing this, stop and think. Do you really want to build a panopticon?

A Panopticon of Internet Connected Things

The idea of the panopticon is a relatively old idea. A panopticon was a building where all (pan-) the residents could be observed (-opticon). If that sounds a little creepy, consider it was originally meant as a design for a prison...

It's actually been implemented in both the real world and in fiction. In the real world, it's been implemented as prisons in a variety of places around the world... In fiction, the most recent mainstream "up-beat" example is the floating prison in Captain America Civil War. The most and well known realisation of the idea of turning the general world into a panopticon is in the world of "big brother" in 1984.

One key point: the purpose of the panopticon is NOT to benefit those staying in the panopticon. The purpose is to benefit the owner of the panopticon in some fashion.

This means that any panopticon of things is designed to benefit the person running the panopticon, not the person who owns the things (however well intentioned the maker was/is). Indeed, it can mean the panopticon views you and your things as a product to be sold (advertising, data, etc), not as customers to provide value to. This isn't be universally the case, but it's common enough.

I don't buy products to benefit some random company. Do you? I buy or use products either for my benefit or for the benefit of those I buy them for. Don't get me wrong, being able to opt-in can have benefits. Google maps being able to give you a different route based on real time data is useful.

But be clear - it's based on using data from a panopticon, built on internet connected things.

Obsolescence Really Means Junk

Internet connected things isn't really a new idea. That means we've now gone through the full product cycle more than once. Be it a Nabaztag, Mattel IM-ME, AIBO, or similar. I've picked these ones for a variety of reasons:

  • They might have been acclaimed
  • The manufacturer thought it was a "Big" thing, and mass produced them
  • They seemed hackable and interesting
  • They're all kinda fun or interesting from some angle, but aren't really now

They all relied on some form of central service, and as those services disappeared, they became less useful or in some cases instantly useless junk. I also picked them because they all had many active hacker groups work to make them useful for many years - often in ways the original manufacturers didn't consider.

For each of these, there are dozens of other active objects with similar issues. They all relied on some form of central service. They all became obsolete when the service they relied on to work disappeared. They all contained interesting tech.

These devices all became junk. The value was in the service, not in the device. Even though with all of these devices they had value to the owner, and could've retained value without the central service.

A Panopticon of Internet Connected Junk

So this is really what this sort of internet of things really means. Building a network of benefit to the owner of the network using devices that become useless when the owner decides to cease supporting the devices.

That means the creation of electrical junk, that is wasteful, and in the end of limited benefit to the customer.

Reframing the question.

Rather than ask "what is the internet of things", ask yourself - "What is the internet of my things?" "what should the internet of things be -- for me?". Or I could ask you "what is the Internet of Things of Yours" ?)

  • Tracking of my "stuff"
  • Monitoring and controlling my own devices which are networked
  • Taking my networks at home and using them all in a unified manner
  • Allowing my devices to work with each other.
  • Using my data and devices in ways that benefit me, and those I get them for.

These are somewhat different answers. Similar, but different. They don't preclude working with panopticons. But they do take a different angle.

This reframed question is the reason behind:

I'll be describing in a short series of posts:

  • IOT-KIT and its motivation
  • How to use IOT-KIT to make your own IOT devices that have longevity of value to the owner.
  • IOTOY specifications
    • Device layer
    • Web Layer
  • An overview of how this was implemented in the microbit prototype
  • How to implement this in your own systems.

The core underlying ideas were:

  • Suppose you can make an arduino (or similar) based device. You should be able to make it an IOT-KIT based device trivially. (ie low barrier to entry)

  • Suppose you know very limited python, can you use and control the IOT devices that sit on your network. (Note, this allows you to then trigger behaviour between devices)

  • No "centre". Minimal standard interfaces making it normal for individuals, groups, companies and consortia to create their own domain specific standards. (Much like these days we use JSON, rather than centralised XML schemas for many services...)

  • Plan for "obsolescence means ongoing utilty". If your devices can continue to remain useful after the manufacturer disappears, then you build value, not junk.

These goals are effectively all designed for a low barrier to entry, while still inter-operating.

If you're interested, please like, share, comment or similar, and as always feedback welcome.

Read and Post Comments