Arduino Messwerte nach Google Analytics

Laser Analytics

Laser Analytics

Der Aufmacher meines Workshops auf der SEO Campixx 2015 war eine laserbasierte Lichtschranke (Laser Analytics), die beim Durchschreiten der Tür zum Raum ein Event an ein Google Analytics-Konto sendet. Mit Tesa Powerstrips waren die Komponenten am Türrahmen angeklebt. Jeder Besucher des Workshops löste ein Event aus und wurde per Beamer im „Google Analytics Live Events Dashboard“ angezeigt.

Elektrik / Schaltung

Fotowiderstand in Plastikkappe

Fotowiderstand in Plastikkappe

Die Elektrik für dieses Projekt ist kein Kunststück. Für gute Lichtschranken kann man zwar durchaus ein paar Euro ausgeben. Hier ging es aber um einen Proof of Concept. Daher habe ich mir etwas hemdsärmelig geholfen. Als Empfänger nutze ich einfach einen Fotowiderstand (Elektrik). Billig und einfach. Den Fotowiederstand habe ich noch in einen schwarzen Plastikdeckel gesetzt um Streulicht von der Seite zu vermeiden. Dazu noch etwas Schrumpfschlauch und Heiss-Kleber sowie Plastikreste (um mir eine Halterung zu bauen).

Ob ich als Sender z.B. einen Laser oder eine Taschenlampe verwende ist am Ende des Tages wurst. Ich hatte noch einen kleinen Laser rumliegen. Diese sind punktuell sehr hell und eignen sich damit an für sich ganz gut. Wir messen die Helligkeit die auf den Widerstand trifft.

Beschaltung  Fotowiderstand

Beschaltung Fotowiderstand

Da der Laser sehr hell ist ist der Wert auch sehr hoch (den wir messen würden). Wird der Laser unterbrochen liegt der Wert bei dem was unsere mehr schlecht als rechte Abschirmung an Restlicht zulässt. Auf jeden Fall aber deutlich drunter (solange unser Sensor nicht auf die Sonne ausgerichtet ist).

Jedenfalls habe ich den Laser mit Heisskleber an einen Batteriehalter geklebt und noch einen Kippschalter dazu gepackt. So hatte ich einen handlichen Laser, der man gut an einen Türrahmen kleben konnte. Die Beschaltung ist hier glaube ich nicht erwähnenswert.

Ablauf

Wir lesen kontinuerlich die Helligkeit am Sensor, fällt diese ab, rufen wir eine Google Analytics URL auf. Damit wir bei unterbrochenem Lichtstrahl nicht kontinuierlich Tracking-Events senden, warten wir, bis es am Sensor wieder richtig hell geworden ist (der Laser-Strahl also den Fotowiderstand getroffen hat). Den erneuten Helligkeitsabfall melden wir dann als neues Event. Würde man zwei Laser-Schranken verwenden, könnte man noch zusätzlich die Richtung auswerten. Gehen zwei Personen nebeneinander durch die Schranke, würden sie als eine Person gezählt.

Google Analytics Measurement Protocol

Hier die Übersicht in Englisch. Wir können Seitenbesuche, Events oder sogar Transactionen senden.

Use-Cases:
Die Besucher eines Ladenlokals zu messen und diese Werte in Google Analytics zu haben ist eine Möglichkeit. Auch könnte man z.B. die Tagestemperatur oder Regenmenge mit ins Analytics-Konto schreiben. Bei einem Pizzadienst könnte man so Essenbestellungen mit dem Wetter korrelieren und dies zur Einkaufsplanung nutzen. Die Idee ist Dinge, die wir in der echten Welt messen können ins Google Analytics Konto zu verfrachten. Dies können wie erwähnt z.B: analoge Werte wie Temperatur, Luftfeuchte, Wind, Helligkeit etc oder auch Währungs-Beträge sein. Man ist hier nicht auf „Seitenaufrufe“ begrenzt. Es lohnt sich, das verlinkte Dokument einmal querzulesen.

Netzwerkverbindung herstellen

Hier ist es im Grunde zum Projekt Wassersäule identisch. Ggf. macht es aber Sinn DHCP zu aktivieren. Wir wollen das Gerät vermutlich irgendwo hinbappen und dort soll es einfach nur senden. Da wir nur sehr wenig Code brauchen ist der Mehrverbrauch an Speicherplatz egal. Mehr Infos zum Initialisieren der Netzwerkverbindung (Ethernet.begin()) in Englisch auf der Arduino Seite.


void setup() {
pinMode(gate, INPUT);
// Open serial communications and wait for port to open:
Serial.begin(9600);
// start the Ethernet connection:
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// try to congifure using IP address instead of DHCP:
Ethernet.begin(mac, ip);
}
}

Tracking Event senden

Dann brauchen wir noch eine Funktion, mit der wir unser Tracking-Ereignis an Google senden (oder welches Tracking-System auch immer). Wir greifen dafür auf ein paar globale Variablen zu, die wir zum Start festlegen (nicht im Beispiel unten). Mit diesen bauen wir uns unsere Tracking-Anfrage zusammen. Wir übergeben dieser Funktion lediglich das Event-Label und Event-Value (wir senden in diesem Beispiel Events).


void trackEvent(String eventlabel, int sensorvalue) {
Serial.println("about to send tracking event ... ");
if (client.connect(server, 80)) {
// if you get a connection, report back via serial:
Serial.println("connected ... ");
// Make request:
client.println("GET /collect?v=1&tid=" + analyticsAccountID + "&cid=" + analyticsClientID + "&t=event&ec=" + analyticsEventCat + "&ea=" + analyticsEventAct + "&el=" + eventlabel + "&ev=" + sensorvalue + " HTTP/1.1");
client.println("Host: www.google-analytics.com");
client.println("Connection: close");
client.println();
Serial.println(" event sent!");
}
else {
// if you didn't get a connection to the server:
Serial.println("connection failed");
}
}

Der Main Loop

Der Main Loop enthält zwei Dinge: (a) das Wegarbeiten der Analytics API Antwort und (b) das Handling der Lichtschranke.

Wir benutzen eine globale Variable newConnection um zu kennzeichnen, ob wir gerade einen Tracking-Event gesendet haben. Ist der Wert true prüfen wir ob noch Zeichen vorliegen und lesen diese aus dem Speicher. Der Hintergrund ist, dass das Ethernetshield nicht sehr viele Verbindungen gleichzeitig bearbeiten kann. Solange wir noch ungelesene Bytes im Speicher haben, gilt die Verbindung als offen (näheres dazu im Projekt Wassersäule). Google Antwortet uns ja zumindest mit einem Status 200 und ein paar HTTP Headern, die sollten wir auslesen, auch wenn wir damit erstmal nichts machen.

Ist gerade kein Tracking im Gange (also newConnection == false) lesen wir die Helligkeit von unserem Fotowiderstand per analogRead(). Als Beispiel habe ich den Schwellwert 500 gewählt. Ist der Wert höher, gehe ich davon aus, dass der Strahl nicht unterbrochen ist (Lichtschrankenzustand gate_state = false). Ist der Wert dadrunter gehe ich davon aus, dass etwas die Lichtschranke unterbricht (gate_state = true).

Dann wird geprüft ob der Zustand = true ist und ob der letzte Zustand = false war. Dies stellt sicher, dass nicht kontinuierlich Trackingevents gesendet werden, wenn jemand in der Schranke stehen bleibt. Da der Zustand erst durch ein Abarbeiten der Antwort von Google Analytics zurückgesetzt wird, stellen wir sicher, dass die Events nur nacheinander verarbeitet/erhoben werden können. Hier hängt es ein wenig davon ab, worauf ihr abzielt, was ihr messen wollt. Eure Kreativität ist gefragt.

Danach wird der Marker für den Betriebszustand (newConnection) auf true gesetzt (damit als nächstes die Analytics Antwort verdaut wird), die Analytics Client ID hochgezählt (für Uniqueness des Events) sowie der Tracker aufgerufen.

Als letztes wird der aktuelle Lichtschrankenzustand gespeichert, um ihn im nächsten Durchlauf vergleichen zu können.

/////////////////////////////////
// Main Loop
/////////////////////////////////

void loop()
{

if (newConnection == true) {
// if there are incoming bytes available
// from the server, read them and print them:
if (client.available()) {
char c = client.read();
//Serial.print(c);
}

// if the server's disconnected, stop the client:
if (!client.connected()) {
Serial.println("disconnecting.");
client.stop();
newConnection = false;
// do nothing forevermore:
delay(10);
}
} else {

gate_value = analogRead(gate);

if (gate_value >= 500) {
gate_state = false;
} else if (gate_value <=499) {
gate_state = true;
}
if (gate_state == true && gate_state_last == false) {
newConnection = true;
analyticsClientID++;
trackEvent("Workshop-Besuch",gate_value);
Serial.println("Besucher");
delay(500);
}

gate_state_last = gate_state;

}
}

Der gesamte Sketch

Der gesamte Code sieht dann wie folgt aus:


/*

See: https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide
for more options about what you can send to anyltics. You will have to adjust the code
to do this, but this should be easy for anyone.

*/
#include <SPI.h>
#include <Ethernet.h>

/////////////////////////////////
// Ethernet Configuration
// - using Ethernet Shield or Arduino Ethernet
// - wired to pins 10, 11, 12 and 13
// - IP optional if DHCP fails
/////////////////////////////////

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,2,202);

/////////////////////////////////
// Parameters for our request
// - server can either be IP adress
//   or name adress (larger skeptch)
/////////////////////////////////

//IPAddress server(74,125,232,128);  // numeric IP for Google (no DNS)
char   server[]            = "www.google-analytics.com";    // name address for Google (using DNS)
String analyticsAccountID  = "UA-XXXXXXXXX-X";
int    analyticsClientID   = 100;
String analyticsEventCat   = "Arduino%20Events";
String analyticsEventAct   = "Raum";
int    analyticsEventVal   = 0;

// Initialize the Ethernet client library
EthernetClient client;

/////////////////////////////////
// Other global variables
/////////////////////////////////

boolean newConnection = false;

int led1    = 8;
int gate        = A0;
int gate_value  = 0;
boolean gate_state  = false;
boolean gate_state_last = true;

/////////////////////////////////
// Setup Loop
/////////////////////////////////

void setup() {

// Open serial communications and wait for port to open:
Serial.begin(9600);
Serial.println("Arduino - Universal Analytics Event Tracker");
Serial.println("-------------------------------------------");
Serial.println("Using Account ID     : " + analyticsAccountID);
Serial.println("Using Client ID      : " + analyticsClientID);
Serial.println("Using Event Category : " + analyticsEventCat);
Serial.println("Using Event Action   : " + analyticsEventAct);
Serial.println("-------------------------------------------");

pinMode(gate, INPUT);
pinMode(led1, OUTPUT);

// start the Ethernet connection:
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// no point in carrying on, so do nothing forevermore:
// try to congifure using IP address instead of DHCP:
Ethernet.begin(mac, ip);
}
// give the Ethernet shield a second to initialize:
delay(1000);

}

/////////////////////////////////
// Main Loop
/////////////////////////////////

void loop()
{

if (newConnection == true) {
// if there are incoming bytes available
// from the server, read them and print them:
if (client.available()) {
char c = client.read();
//Serial.print(c);
}

// if the server's disconnected, stop the client:
if (!client.connected()) {
Serial.println("disconnecting.");
client.stop();
newConnection = false;
// do nothing forevermore:
delay(10);
}
} else {

gate_value = analogRead(gate);

if (gate_value >= 500) {
gate_state = false;
} else if (gate_value <=499) {
gate_state = true;
}
if (gate_state == true && gate_state_last == false) {
newConnection = true;
analyticsClientID++;
trackEvent("Workshop-Besuch",gate_value);
Serial.println("Besucher");
delay(500);
}

gate_state_last = gate_state;

}
}

/////////////////////////////////
// The actual Tracking call
/////////////////////////////////

void trackEvent(String eventlabel, int sensorvalue) {
Serial.println("about to send tracking event ... ");

// if you get a connection, report back via serial:
if (client.connect(server, 80)) {
Serial.println("connected ... ");
// Make a HTTP request:
client.println("GET /collect?v=1&tid=" + analyticsAccountID + "&cid=" + analyticsClientID + "&t=event&ec=" + analyticsEventCat + "&ea=" + analyticsEventAct + "&el=" + eventlabel + "&ev=" + sensorvalue + " HTTP/1.1");
client.println("Host: www.google-analytics.com");
client.println("Connection: close");
client.println();
Serial.println(" event sent!");
}
else {
// kf you didn't get a connection to the server:
Serial.println("connection failed");
}
}

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert