electric Imp

Der electric Imp ist ein kleiner Microcontroller mit eingebautem WLAN Adapter auf der Größe einer normalen SD Karte, wie man sie von Digitalkameras her kennt:

Als SD Karte ist der electric Imp alleine zu Nichts zu gebrauchen, man sollte ihn auch nicht in einen normalen SD Kartenslot stecken. Im besten Fall geht nichts kaputt, im schlimmsten Fall ist der electric Imp nachhaltig beschädigt. Zusätzlich zum electric Imp (eImp) bedarf es eines Breakouts oder eines anderen sogenannten “Impee”, wie die Entwickler des eImp ein Gerät nennen, welches einen eImp aufnehmen kann. Für diesen Review wurde ein Arduino Shield für den eImp gewählt, welches von Sparkfun stammt.

In den Beispielcodes wird im WIki von eImp immer wieder von “April” und “Hannah” gesprochen. Es handelt sich dabei um Impees, welche von eImp designed worden sind und an Betatester vergeben wurden. Ich habe bislang keine Quelle gesehen, welche das Impee Hannah anbietet. Man kann sie aus den angebotenen Dateien aber selbst herstellen, was beim Hannah vielleicht sogar sehr interessant wäre.

Erste Schritte - Das Shield

Als erstes wurde das Shield mit Headern versehen, die getrennt gekauft werden müssen. Sparkfun begründet dies damit, dass das Shield die freie Wahl des Headers ermöglichen soll, was bei den geringen Kosten der Header keine schlechte Idee ist. So kann man sich entscheiden, stackable Headers oder eine Stiftleiste einzubauen. Empfehlenswert ist es, die innen gelegene Spiegelung der Kontakte mit einer Buchsenleiste zu bestücken.

Arduino Shield

Schaut man sich die Beschreibung des Shields and, dan sieht man, dass das Shield bis auf RX/TX keine Verbindung zu den Arduino-Pins herstellt, es also lediglich eine Aufnahme für den eImp darstellt.

 

Lediglich die Pins 8 und 9 sind mit LED bestückt und auf die Arduino-Pins 8 und 9 gelegt. Diese Belegung kann durch Trennen der Lötbrücken geändert werden. Die zwei LED sind miteinem Vorwiederstand geschaltet, ähnlich wie die LED auf dem Arduino mit D13 verbunden ist

Das eImp ist auf 3.3 Volt ausgelegt und wird beschädigt, wenn man auf seine Eingänge ein 5 Volt Signal legt. Hier muss ein Levelshifter zwischengeschaltet werden oder man steckt das Shield auf ein 3.3 Vol Board, wie es der neue Arduino Due darstellt (alternativ chipKit).

Dem Schaltplan kann man auch entnehmen, dass die serielle Kommunikationsleitung (RX/TX) über einen Levelshifter Mosfet 5 Volt tolerant ausgelegt sind. RX und TX sind auf D8 und D9 gelegt und können mit Brücken auf D0 und D1 umgestellt werden. Diese befinden sich auf der Unterseite des Shields als Lötbrücken.

Dann mal los ...

Als erstes stellt sich die Frage, wie man nun mit dem eImp kommunizieren soll. Dazu muss man ihn erst einmal “Commissionieren”, also bei electric Imp anmelden. Dazu bedarf es eines Accounts bei electric Imp, was aber nach Eingabe eines Usernamens, der e-mail adresses und eines Passworts problemlos funktioniert.

Dann wird es interessant. Denn jetzt braucht man ein iOS oder Android Mobiltelefon, denn der eImp wird über eine Folge von Lichtblitzen konfiguriert! Dazu läd man die entsprechende App, meldet sich mti dem neu angelegten Account an und gibt die WLAN Informationen in der Maske ein. Bei normalen privaten WLANs ist das kein Problem, WLAN, welche mit userid/password arbeiten werden noch nicht unterstützt. In meinem Fall war es ein einfacher Router mit WPA2, so dass nur die SSID und das Passwort eingegeben werden musste.

Der unkonfigurierte eImp blinkt orange und man kann an der Vorderseite ein kleine Öffnung erkennen, wo der EMpfänger für die Daten sitzt. Man muss beim Empfangen die Oberseite des eImp mit dem Finger abdecken, da das orangene Licht den Empfang stört. Man sollte das Mobile so nah wie möglich an den eImp halten. Tippt man den Send Button an, startet das Mobile kurz danach das Senden der Konfiguration indem es zu flackern beginnt. Werden die Daten verstanden, so blinkt das eImp kurz grün und versucht dann eine Verbindung mit dem WLAN herzustellen.

Leider hat das auf Anhieb nicht funktioniert. Erst nach vielen Versuchen kam ich auf die Idee, das Mobiltelefon um 90 Grad zu drehen, was auf Anhieb die Konfiguration transferierte. Hier scheint es sich um einen Polarisationseffekt zu handeln, der natürlich vom verwendeten Telefon abhängt (Galaxy Nexus).

Danach erscheint das angemeldete eImp sofort im Planner. Die Antwortzeit war erstaunlich kurz, was aufgrund der Netzverbindung (4 Mbps) nicht zu erwarten war.

Das erste Programm

Die erste Hürde, die für das Erstellen des ersten Programmes überwunden werden muss, ist die neue Programmiersprache: Squirrel

Als IDE wird der Browser verwendet, denn die gesamte Programmierung erfolgt in der Cloud auf der Entwicklerplatform Planner, welche von der Registrierung her schon bekannt ist. Das bedeuet aber auch, dass man den eImp nicht offline editieren kann sondern dazu immer mit dem Internet verbunden sein muss.

Die Sprache ist an C bzw. Javascript angelehnt und sieht auf den ersten Blick vertraut aus, wenngleich es keine explizite Typdeklationen zu geben scheint. Am einfachsten ist es, mit einem Beispielprogramm zu beginnen: die Implementiereung des blink Sketches wie vom Arduino her bekannt.

// Pin 8/9 blink
// using the shield LED on pin 8 and pin 9 to blink alternatively
// Variable to represent LED state
ledState1 <- 0;
ledState2 <- 1;
// blink function called every 100ms
function blink()
{
    // Change state
    ledState1 = ledState1?0:1;
    ledState2 = ledState2?0:1;
 
    // Reflect state to the pin
    hardware.pin9.write(ledState1);
    hardware.pin8.write(ledState2);
 
    // Schedule the next state change
    imp.wakeup(0.1, blink);
}
// Configure pin 9 as an open drain output with internal pull up
hardware.pin9.configure(DIGITAL_OUT_OD_PULLUP);
// Configure pin 8 as an open drain output with internal pull up
hardware.pin8.configure(DIGITAL_OUT_OD_PULLUP);
 
// Register with the server
imp.configure("Blinking Shield", [], []);
 
// Start blinking
blink();
 
// End of code.

 

Als erstes werden zwei variablen initialisiert, welche die LED-Zustände speichern. Zwei LED desshalb, weil sich auf dem Shield zwei LED befinden, welche mit den Pins 8 und 9 verbunden sind.

Die Funktion blink() schaltet die Zustände um, wobei man hier schön die Unterschiede zu Arduino C erkennen kann: Beim Arduino würde man schreiben

ledState1 = !ledState1;

und hier findet man eine implizite Bedingung

ledState1 = ledState1?0:1;

bei der im Falle, dass ledState1 1 ist eine 0 zurückgegeben wird, ansonsten eine 1. Das kann unter Umständen verwirrend sein, sollte einen aber nicht mehr befremden als wenn man ständig zwischen Basic und C wechselt.

Die hardware Befehle sind selbsterklärend und der Aufruf imp.wakeup entsprichet dem delay(), nur dass die Zeit in Sekunden angegeben wird.

Sobald man den Code ausführt (run code), wird dieser auf den eImp via dem Internet übertragen. Dies geschah auch wieder regelrecht instantan und mein eImp begann zu blinken.

Vorläufiges Fazit

Die ersten Schritte gestalteten sich sehr einfach, wenn man von den Problemen mit der Konfiguration absieht. Über das Forum wurde mit dem Zeitverzug zur USA recht schnell geantwortet wobei dieser Polarisationseffekt noch unklar ist. Die Handhabung des eImp und die Entwicklung von Code im Browser sowie die neue Sprache sind ungewöhnlich aber man kommt schnell damit zurecht. Der eImp hat ein gewaltiges Potential, das es nun zu erkunden gilt.

Den Kauf bereue ich bisher in keiner Weise, ganz im Gegenteil, ich empfehle eher, sich gleich zwei eImps zu besorgen und dann zwei Arduinos zu verbinden.

Das wird auch der Inhalt des zweiten Programs: Der eImp soll mit dem Arduino, der den Shield trägt, kommunizieren.

Arduino an eImp...

 

Der nächste Schritt ist nun, den eImp vom Arduino direkt anzusprechen. Dazu verwende ich ein einfaches Sketch, welches via SoftwareSerial einen periodischen String an den eImp schickt:

#include <SoftwareSerial.h>
 
SoftwareSerial impSerial(8,9);
 
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  impSerial.begin(9600);
}
 
int loopc = 0;
void loop() {
  // put your main code here, to run repeatedly: 
  Serial.print("loop: ");
  Serial.println(loopc);
  impSerial.print("loop: ");
  impSerial.println(loopc);
  delay(2000);
  loopc++;
}

Auf dem eImp wartet ein Code auf Input vom UART port (der Code wurde dem Tutorial von Sparkfun entnommen und ein wenig angepasst):

// Transmit data between UART and Input/OutputPorts on the impee
// by: Jim Lindblom
//     SparkFun Electronics
// date: September 26, 2012
// license: BeerWare
//          Please use, reuse, and modify this code as you need.
//          We hope it saves you some time, or helps you learn something!
//          If you find it handy, and we meet some day, you can buy me a beer or iced tea in return.
 
local rxLEDToggle = 1;  // These variables keep track of rx/tx LED toggling status
local txLEDToggle = 1;
 
// impeeIn will override the InputPort class. 
// Whenever data is received to the impee, we'll jump into the set(c) function defined within
class impeeIn extends InputPort
{
    name = "UART Out";
    type = "string";
 
    // This function takes whatever character was sent to the impee
    // and sends it out over the UART5/7. We'll also toggle the txLed
    function set(c)
    {
        hardware.uart57.write(c);
        toggleRxLED();
    }
}
 
local impeeInput = impeeIn();  // assign impeeIn class to the impeeInput
local impeeOutput = OutputPort("UART In", "string");  // set impeeOutput as a string
 
function initUart()
{
    hardware.configure(UART_57);    // Using UART on pins 5 and 7
    hardware.uart57.configure(9600, 8, PARITY_NONE, 1, NO_CTSRTS); // 19200 baud worked well, no parity, 1 stop bit, 8 data bits
}
 
function initLEDs()
{
    // LEDs are on pins 8 and 9 on the imp Shield
    // They're both active low, so writing the pin a 1 will turn the LED off
    hardware.pin8.configure(DIGITAL_OUT_OD_PULLUP);
    hardware.pin9.configure(DIGITAL_OUT_OD_PULLUP);
    hardware.pin8.write(1);
    hardware.pin9.write(1);
}
 
// This function turns an LED on/off quickly on pin 9.
// It first turns the LED on, then calls itself again in 50ms to turn the LED off
function toggleTxLED()
{
    txLEDToggle = txLEDToggle?0:1;    // toggle the txLEDtoggle variable
    if (!txLEDToggle)
    {
        imp.wakeup(0.05, toggleTxLED.bindenv(this)); // if we're turning the LED on, set a timer to call this function again (to turn the LED off)
    }
    hardware.pin9.write(txLEDToggle);  // TX LED is on pin 8 (active-low)
}
 
// This function turns an LED on/off quickly on pin 8.
// It first turns the LED on, then calls itself again in 50ms to turn the LED off
function toggleRxLED()
{
    rxLEDToggle = rxLEDToggle?0:1;    // toggle the rxLEDtoggle variable
    if (!rxLEDToggle)
    {
        imp.wakeup(0.05, toggleRxLED.bindenv(this)); // if we're turning the LED on, set a timer to call this function again (to turn the LED off)
    }
    hardware.pin8.write(rxLEDToggle);   // RX LED is on pin 8 (active-low)
}
 
// This is our UART polling function. We'll call it once at the beginning of the program,
// then it calls itself every 10us. If there is data in the UART57 buffer, this will read
// as much of it as it can, and send it out of the impee's outputPort.
function pollUart()
{
    imp.wakeup(0.1, pollUart.bindenv(this));    // schedule the next poll in 10us
    local bcnt = 0;
    local s = "";
    local byte = hardware.uart57.read();    // read the UART buffer
    // This will return -1 if there is no data to be read.
    while (byte != -1)  // otherwise, we keep reading until there is no data to be read.
    {
        s+=byte.tochar();
//        s+=byte;
        bcnt++;
        //  server.log(format("%c", byte)); // send the character out to the server log. Optional, great for debugging
        // impeeOutput.set(byte);  // send the valid character out the impee's outputPort
        byte = hardware.uart57.read();  // read from the UART buffer again (not sure if it's a valid character yet)
        toggleTxLED();  // Toggle the TX LED
        // server.show(byte);
    }
    if (bcnt > 0){
//        server.show("String received");
        server.show(s);
        impeeOutput.set(s);
    } 
}
 
// This is where our program actually starts! Previous stuff was all function and variable declaration.
// This'll configure our impee. It's name is "UartCrossAir", and it has both an input and output to be connected:
imp.configure("UartCrossAir", [impeeInput], [impeeOutput]);
initUart(); // Initialize the UART, called just once
initLEDs(); // Initialize the LEDs, called just once
pollUart(); // start the UART polling, this function continues to call itself
// From here, two main functions are at play:
//      1. We'll be calling pollUart every 10us. If data is sent from the UART, we'll send out out of the impee.
//      2. If data is sent into the impee, we'll jump into the set function in the InputPort.
//
// The end

 

Der Output sieht dann so aus:

 

Servos steuern

Auf instructables.com habe ich eine ANleitung gesehen, wie man einen Pan/Tilt mit zwei Servos über den eImp steuert. Dazu werden die beiden Servos mit Vcc (rot) und GND verbunden (braun) und der Signaldraht auf Pin1 bzw. Pin2 des eImp. Der Code ist erstaunlich kurz:

// Controlling Servos with PWM on pins 1 and 2

/*
 * input ports set position of servos 1 and 2, respectively
 *
 * T. Buttner 11/07/12
 *
 */
 
/* Pin Assignments
 * Pin 1 = PWM for servo 1
 * Pin 2 = PWM for servo 2
 */
 
// Configure hardware

// the servos used in this example have ~170 degrees of range. Each servo has three pins: power, ground, and pulse in
// The width of the pulse determines the position of the servo
// The servo expects a pulse every 20 to 30 ms
// 0.8 ms pulse -> fully counterclockwise
// 2.6 ms pulse -> fully clockwise
// set up PWM on both pins at a period of 20 ms, and initialize the duty cycle
hardware.pin1.configure(PWM_OUT, 0.02, 0.045);
hardware.pin2.configure(PWM_OUT, 0.02, 0.045);

server.log("Hardware Configured");

class Servo extends InputPort
{
    type = "float"
    pin = null
    
    // define a constructor so that we can construct seperate instances for servos 1 and 2
    constructor(name, pin) {
        // call through to the base (InputPort) constructor with the provided name
        base.constructor(name)
        this.pin = pin
        // no need to configure the pins as we've already done it at global scope
    }
    
    function set(value) {
        // since pin 1 is configured for PWM, we can set the duty cycle with pin.write()
        // write a floating point number between 0.0 and 1.0, where 1.0 = 100% duty cycle
        this.pin.write(0.04 + (value * 0.09));
        server.log(format("%s set to %.2f", name, value));
    }
}

// imp.configure registers us with the Imp Service
// The first parameter Sets the text shown on the top line of the node in the planner - i.e., what this node is
// The second parameter is a list of input ports in square brackets
// The third parameter is a list of output ports in square brackets
imp.configure("eimp Dual Servo Controller", [Servo("Pan", hardware.pin1), Servo("Tilt", hardware.pin2)], []);

//EOF

Als Input kann man über "Add Node" eine sogenannte Softnode einfügen. Ich wähle die Node "TickTock" welche im Sekundentakt zwischen 0 und 1 hin und her springt. Die Node hat an der Seite ein Tab mit einem Pluszeichen. Wenn man das auf den eImp zieht erscheint eine Auswahl, welchend er beiden Kanäle man verbinden will. Das Ganze ist selbsterklärend und einfach. Danach sieht es im Planner dann so aus:

Quasi instantan beginnen die beiden Servos zwischen den Maximalstellungen hin und her zu wechseln. Das Ganze hat keine 5 Minuten gebraucht und die meiste Zeit verbrachte ich damit, die Servos zu verdrahten.

I2C Sensor auslesen

Als letztes gehe ich nun daran, einen Sensor über I2C auszulesen. Dazu verwende ich einen LM75 Temperatursensor, der aufgrund seiner Einfachheit der beste Kandidat ist. Ausserdem gibt es ein herrliches tutorial von tronixstuff für den Arduino, der mir die notwendige Informationen liefert.

Die Addresse ist beim electric Imp 8 bit lang, so dass man die arduino-addresse 0x48 um eine Stelle nach Links shiften muss, also die Addresse 0x90 verwenden muss (alle Address-bits des LM75 sind hier auf GND = 0)

Die Verkabelung ist einfach, wobei ich die Information, wo SDA und SCL angeschlossen werden soll, im Web nicht finden konnte. Daher hier:

  • SCL = pin 1
  • SDA = pin 2
  • Vcc = 3.3V (oder levelshifter einsetzen)

Der Code gestaltet sich wieder recht simpel:

// LM75 (CN75) Temperature Monitor using I2C 
hardware.configure(I2C_12);
local i2c = hardware.i2c12;
 
local out_temp  = OutputPort("Temperature");
imp.configure("Temperature LM75", [],[out_temp]);
 
function getsample() {
    imp.wakeup(1.0, getsample.bindenv(this));
    // Start conversion
    i2c.write(0x90, "");
    i2c.write(0x90, "0");
 
    // Wait at least 36ms
    imp.sleep(0.05);
 
    local a = 0.0;
    local temperature = 0.0;
    a = i2c.read(0x90, "\x00", 2);
 
    if (a==null){
        temperature=999;
    } else {
        if (a[0]>127){
            temperature = (a[0]-127)*-1.0; // values above 127 are negative degrees
            if (a[1]==128) temperature -= 0.5;
        } else {
            temperature = a[0];
            if (a[1]==128) temperature += 0.5;
        }
    }
 
    // log and print
    server.log(format("temperature %.2f C ",temperature));
    server.show(format("%.2f C", temperature));
 
    //Send data to server
    out_temp.set(temperature);
}

getsample();

Man sieht, dass die Handhabung der I2C Kommunikation hinreichend einfach ist. Somit stehen einem die ganze Vielfalt der I2C Produkte zur Verfügung.

Fazit

Der electric Imp ist ein faszinierendes Stück Hardware, das mittels minimalem Aufwand eine Brücke ins Internet schlägt. Am besten ist es, wenn man zwei electric Imp hat und damit zwei Arduinos über das Internet miteinander reden lässt.

Die Sprache ist etwas gewöhnugsbedürftig und man muss isch erst einmal in die Arbeitsweise des electric Imp eingewöhnen, aber es ist den Aufwand wert.

Ich gehe mir jetzt einen zweiten electric Imp besorgen ...