Fischertechnik
AVR
Raspberry Pi
Elektronik
Netzwerk
Sonstiges


















Impressum

Particle P0 Photon

Photon
Ein wesentlicher Nachteil der einfachen Arduino-Module ist für mich zunehmend die fehlende Integration im Netzwerk. Die billigen Ergänzungsmodule sind ok, aber mehr auch nicht. Mit dem Photon gibt es schon etwas länger ein Modul ohne dieses Handicap.

Kurzbeschreibung

Das Modul hat 24 Anschlüsse (dual inline), also DIP24. Acht digitale Ein-/Ausgänge, sechs analoge, Tx/Rx, Reset und Strom. Im einfachsten Fall erfolgt die Stromversorgung über den eingebauten USB-Anschluss.
Der Clou ist das integrierte WLAN-Modul.
Für mein Interesse war die Möglichkeit das Modul in einen Tiefschlaf versetzen zu können mit ausschlaggebend. Stromverbrauch dann unter 0,01 mA.

Am USB-Port unter Linux

Die liebgewonnen Ausgaben Serial.print() und so fort funktionieren unter Linux auf Anhieb. Nach dem Anstecken des Moduls gibt es /dev/ttyACM0, das zum Beispiel mit stty -F /dev/ttyACM0 9600 eingestellt werden kann. Mit cat /dev/ttyACM0 werden dann die Ausgaben dargestellt.

Erstinbetriebnahme

Die Erstinbetriebnahme ist sofort gelungen, erfordert aber ein echtes Umdenken.
  1. Smartphone erforderlich
  2. Particle-App auf dem Smartphone installieren
  3. Auf der Plattform registrieren
  4. Modul mit Strom versorgen (z.B. USB)
  5. In der App (selbsterklärend) das Hinzufügen eines neuen Photons auswählen
  6. Der Photon bietet in dieser Phase ein eigenes WLAN an, dieses wird unter Android automatisch gewählt, unter IOS muss dieses manuell ausgewählt werden.
  7. Dann die Auswahl des eigenen WLAN aus der Liste der (vom Smartphone) sichtbaren Netze
  8. Jetzt meldet sich der Photon in seiner Cloud an
  9. Im Internet erscheint der Photon dann in der Online-Entwicklungsumgebung
  10. "Blink" sieht aus wie bei Arduino. Die LED hängt hier an D7 und nicht D13.

Erste Internet-Anwendung

Fällt die Heizung in Abwesenheit aus, besteht das Risiko, dass das Heizungswasser in den Rohren friert und die Rohre platzen. Nach dem Auftauen läuft die Brühe dann ins Haus.
Unabhängig von der Eintrittswahrscheinlichkeit fährt sich leichter weg, wenn im Internet zu sehen ist, dass alles in Ordnung ist.
Der Schaltungsansatz war einfach: Ein Photon, mindestens zwei Temperaturfühler auf Basis DS18B20 und eine Internetanbindung, um die gemessenen Temperaturen auch aus der Ferne betrachten zu können. Dabei sollten die Daten im Internet gespeichert werden, damit das Modul die meiste Zeit, nahezu ohne Stromverbrauch, schlafen kann.
Die Luxusvariante überwacht dann noch die Außentemperatur und einen Raum im Haus.
Photon-Temperaturmodul

Realisierung

Der Platinenabschnitt im Bereich der WLAN-Antenne sollte uneingeschränkt frei bleiben, deshalb habe ich nur die Ports D5 und A5 genutzt und kam mit einem kleinen Streifen Lochrasterplatine aus.
Tatsächlich nutze ich VIN und GND (ich zähle das mal als Pin 1 und Pin 2), A5 (Pin 7), D5 (Pin 18), GND (Pin 21) und 3V3 (Pin 24).
An VIN und GND habe ich die Möglichkeit einen meiner Einzellen- LiPos direkt anzuschließen. A5 nutze ich als digitalen Eingang mit Pullup. Über einen Jumper kann ich den Tiefschlaf verhindern und so das Modul auch mal neu programmieren.
Auf der anderen Seite habe ich ein Pfostenfeld 4x3 (Platz ist für 5x3) für den Anschluss der Temperatursensoren aufgelötet. Die mittlere Reihe ist an 3V3, die dem Modul zugewandte Reihe an GND und die abgewandte Reihe ist an D5 und über einen 4k7 Pullup mit 3V3 verbunden.
Das Modul gibt es in zwei Varianten; hier habe ich die "no headers"-Variante eingesetzt. Zur Montage habe ich Sockelleisten verwendet, die gerade so durch die Modulplatine und die Lochrasterplatine reichen. Auf diese Art und Weise spare ich Platinenfläche und kann trotzdem zu Mess- oder Erweiterungszwecken alle Anschlüsse leicht abgreifen.
Diese kompakte Bauweise hat den Nachteil, dass die Taster auf dem Modul nicht mehr gut bedienbar sind. Darüberhinaus sind die Lötstellen zwischen Sockelleiste und Modul nur schwer kontrollierbar und auf der fertigen Platine kaum nachzulöten.

Quelltext

Wer die Sensoren unter Arduino schon benutzt hat, kommt sofort klar. Das Standard-Beispiel läuft auch auf dem Photon. Da Fehlmessungen zum Leben dazugehören, mache ich fünf Messungen und verwerfe den kleinsten und größten Wert. Die verbleibenden mittele ich.
Der Aufruf des Web-Zugriffs einschließlich Parameterübergabe sollte selbsterklärend sein. Im Quelltext hat der Web-Server die IP-Adresse 1.2.3.4 und heißt host.domain.
#define NoDevelopment   A5
#define MAXSENSORS      10
#define MAXITERATIONS   5
// This #include statement was automatically added by the Particle IDE.
#include "lib1.h"
// Use this include for the Web IDE:
#include "OneWire/OneWire.h"

// Use this include for Particle Dev where everything is in one directory.
// #include "OneWire.h"


TCPClient httpClient;
byte server[] = { 1, 2, 3, 4 }; // IP-Adresse des Servers


void sendOneSensor(char *addr, float celsius)
{
  if (httpClient.connect(server, 80)) {
    httpClient.print("GET /cgi-Pfad/cgi-name?");
    httpClient.print(addr);
    httpClient.print(":");
    httpClient.print(celsius);
    httpClient.println(" HTTP/1.0");
    httpClient.println("Connection: close");
    httpClient.println("Host: host.domain");
    httpClient.println("Accept: text/html, text/plain");
    httpClient.println();
    }

  else
  {
    Spark.publish("status", "connection failed");
    httpClient.stop();
  }

  while (httpClient.connected())
  {
    while (httpClient.available())
    {
      char c = httpClient.read();
      //Serial.print(c);
    }
  }

  httpClient.flush();
  httpClient.stop();

}


char line[240];
OneWire ds(D5);  // on pin D5 (a 4.7K resistor is necessary)

void setup(void) {
    pinMode(NoDevelopment, INPUT_PULLUP); 
}

void loop(void) {
  byte i,iteration;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  byte count=0;
  float celsius,sensorvalue[MAXITERATIONS][MAXSENSORS];
  char address[MAXSENSORS][20],deviceType[MAXSENSORS][100];;
  ds.reset_search();
  while(ds.search(addr) && (count<MAXSENSORS) ){
    if (OneWire::crc8(addr, 7) != addr[7]) {
        Particle.publish("Messung", "CRC is not valid!", 1000);
        continue;
    };
      // the first ROM byte indicates which chip
    switch (addr[0]) {
      case 0x10:
        sprintf(deviceType[count],"DS18S20");  // or old DS1820
        type_s = 1;
        break;
      case 0x28:
        sprintf(deviceType[count],"DS18B20");
        type_s = 0;
        break;
      case 0x22:
        sprintf(deviceType[count],"DS1822");
        type_s = 0;
        break;
      default:
        sprintf(deviceType[count],"Device %02x is not a DS18x20 family device.",addr[0]);
        Particle.publish("Messung", deviceType[count], 1000);
        continue;
    }; // switch
    // 
    sprintf(address[count],"%02x%02x-%02x%02x-%02x%02x-%02x%02x",addr[0],addr[1],addr[2],addr[3],addr[4],addr[5],addr[6],addr[7]);
    for(iteration=0;iteration<MAXITERATIONS;iteration++){
      ds.reset();
      ds.select(addr);
      ds.write(0x44, 1);        // start conversion, with parasite power on at the end

      delay(1000);     // maybe 750ms is enough, maybe not
      // we might do a ds.depower() here, but the reset will take care of it.

      present = ds.reset();
      ds.select(addr);
      ds.write(0xBE);         // Read Scratchpad

      for ( i = 0; i < 9; i++) {           // we need 9 bytes
        data[i] = ds.read();
      };
      // Convert the data to actual temperature
      // because the result is a 16 bit signed integer, it should
      // be stored to an "int16_t" type, which is always 16 bits
      // even when compiled on a 32 bit processor.
      int16_t raw = (data[1] << 8) | data[0];
      if (type_s) {
        raw = raw << 3; // 9 bit resolution default
        if (data[7] == 0x10) {
          // "count remain" gives full 12 bit resolution
          raw = (raw & 0xFFF0) + 12 - data[6];
        }
      } else {
        byte cfg = (data[4] & 0x60);
        // at lower res, the low bits are undefined, so let's zero them
              if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
         else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
        else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
        //// default is 12 bit resolution, 750 ms conversion time
      };
      celsius = (float)raw / 16.0;
      sensorvalue[iteration][count]=celsius;
    };
    count++;
  };
  // alle Sensordaten liegen vor. Streiche min/max und mittele den Rest
  for(i=0;i<count;i++){ // fuer alle Sensoren
    int j;
    byte minpos,maxpos;
    float min=10000.0,max=-10000.0,sum;
    sum=0.0;
    for(j=0;j<MAXITERATIONS;j++){
        if(sensorvalue[j][i]<min){minpos=j;min=sensorvalue[j][i];};
        if(sensorvalue[j][i]>max){maxpos=j;max=sensorvalue[j][i];};
    };
    for(j=0;j<MAXITERATIONS;j++){
        if( (j!=minpos) && (j!=maxpos) ){sum+=sensorvalue[j][i];};
    };
    if(minpos==maxpos)celsius=sensorvalue[0][i]; else celsius=sum/(MAXITERATIONS-2);
    sendOneSensor(address[i],celsius);
    sprintf(line,"%2d %s %s %4.1f %lu",i,address[i],deviceType[i],celsius,millis()/1000);
    Particle.publish("Messung", line, 10000);
  };
  delay(1000);
  if( (digitalRead(NoDevelopment))  )System.sleep(SLEEP_MODE_DEEP,600);
  else delay(600);
}



Photon




























Akku-Betrieb

Die sehr platzsparende Montagevariante der ersten Versionen habe ich inzwischen abgewandelt und hier einen Photon mit Stiftleisten eingesetzt; die Sockelleisten gibt es weiterhin und verbreitern das Design entsprechend um maximal die zwei zusätzlichen Reihen.
Weiterhin bleibt die Steuerung über A5: Steckbrücke gesteckt verhindert die Schlafphasen und ermöglicht die Neuprogrammierung. Entgegen den ersten Versionen wird der Pin A5 jetzt analog genutzt.
Zwischen V_IN und GND ist ein Spannungsteiler, der Eingangsspannungen bis 5 V auf Werte bis 3,3 V herunterteilt. Konkret 1000 kΩ zwischen GND und A5 und dann eine Reihenschaltung aus 470 kΩ und 100 kΩ von A5 zu V_IN. Mit diesen Werten wird eine Eingangsspannung von 5,181 V auf 3,3 V am Pin A5 gewandelt; der zugehörige Strom liegt dann bei 3,3 μA. Die Auflösung beträgt ca. 1,3 mV und teilt den Spannungsbereich der eingesetzten LiIon-Akkus in über 700 Stufen.
Auf dem Bild durch die Steckbrücke verdeckt, ist noch ein Kondensator parallel zum 1000 kΩ-Widerstand; 100 nF aus der Grabbelkiste. Sinn und Zweck ist eine stabile, niederohmige Bereitstellung der Spannung am analogen Eingang. Hintergrund ist das Messverfahren im Prozessor. Dieser lädt vor der eigentlichen Messung einen internen Kondensator über den ausgewählten analogen Eingang. Die gewählten Widerstandswerte im Spannungsteiler sind zwar Akku-schonend, aber viel zu hoch um diesen Kondensator zu laden. Mit dem Zusatzkondensator wird dieses Problem einfach umgangen.

Akku-Kapazität

Die Messung erfolgt mit der aktuellen Software ca. alle 10 Minuten. Bei zwei angeschlossenen Sensoren ist das Board dafür jeweils knapp 20 Sekunden eingeschaltet. Pro Tag sind das also ca. 24*6*20 Sekunden oder 0,8 Stunden. Die Stromaufnahme liegt bei ca. 100 mA; pro Tag wird demnach eine Akkukapazität von 80 mAh benötigt.
Mit einer Standardzelle 18650 mit 2600 mAh sollte ein Monat Laufzeit leicht möglich sein. In Realität gibt es immer wieder Internet-Probleme, die in Kombination mit einem wohl noch immer vorhandenen Firmwarebug, dazu führen können, dass das Modul stundenlang hängt. In der aktuellen Software wird deshalb nach einer Minute Laufzeit der 10-minütige Tiefschlaf gestartet. Es sieht so aus, als wäre das Problem so dauerhaft umgangen.

Klippen

kein WLAN

Mit meinem zweiten Photon hatte ich Pech. Er wollte partout nicht ins Netz und war damit quasi wertlos. Dieses Problem hatten vor mir schon einige und in einem Forum stand dann auch die Lösung für solche Probleme. Ursache war wohl ein defekter Schlüssel. Dieser lässt sich mit den folgenden Schritten beseitigen:
  1. node.js installieren v4.2.3 LTS geht; v5.1.1 Stable nicht.
  2. particle- cli installieren
    npm install -g particle-cli
  3. USB-Treiber für Photon installieren https://s3.amazonaws.com/spark-website/Particle.zip
  4. USB-Treiber für DFU-Mode installieren zadig_2.1.2.exe
  5. dfu-util installieren (ist oft nur herunterladen)
  6. openssl installieren
  7. DFU mode einschalten (gelbes Blinken)
    Strom ein, beide Tasten drücken, RESET loslassen, warten bis gelbes Blinken, SETUP loslassen. Nach gelb kommt grün, dann einfach nochmal probieren.
  8. Alten Schlüssel sichern:
    dfu-util -d 2b04:d006 -a 1 -s 2082:512 -U cloud_public_from_device.der
  9. Neuen Schlüssel generieren:
    particle keys new device.der
  10. DFU mode einschalten (gelbes Blinken)
  11. Neuen Schlüssel auf Photon übertragen:
    dfu-util -d 2b04:d006 -a 1 -s 34 -D device.der
Inzwischen gibt es anscheinend auch eine Vereinfachung particle keys doctor <ID>. Dieses erfordert alle o.a. Tools, spart aber die einzelnen Aufrufe.