# Sensorübetragung von Tablet auf Raspberry Pi



## Alex1820 (7. Sep 2014)

Hallo Zusammen,

anbei präsentiere ich mein aktuelles Projekt und dessen Problematik. Gründe für die Problematiken werden wahrscheinlich sein, dass ich in diesen Gebieten ein blutiger Anfänger bin und hoffe mir daher etwas erfahrene Unterstützung. 

Bei meinem Projekt handelt es sich um eine Steuerungsapp, welche den Neigungssensor meines Smartphone oder Tablets ausliest und diese Werte anschließend an ein Raspberry Pi sendet. Hierbei aggiert dieser als Hotspot und somit als Server, welche die Daten später auf einer Webseite darstellt.

Problematik:
Meine Probleme zur Zeit sind aber bei der Datenübertragung vorhanden. Ich habe eine App erstellt, welche erfolgreich die Sensordaten ausliest. Nun stehe ich vor dem Rätsel wie ich diese Daten am besten über eine Socketverbindung zum Raspberry sende. Ich habe schon viel Zeit aufgewendet im Internet nach Möglichkeiten zu suchen, die zu meinem Projekt passen könnten. Leider war ich noch nicht so erfolgreich, was sicherlich auch daran liegen kann, dass ich vor lauter Wald die Bäume nicht mehr sehe. :roll:
Was vielleicht noch zu erwähnen wäre, wobei ich nicht weiß ob es wichtig ist. die App wurde ursprünglich mit Processing erstellt und nach Android Studio importiert.

Hier könnt ihr mal meinen aktuellen Programmiercode sehen:


```
package processing.test.sensorkipp_pde;
     
    import ketai.sensors.KetaiSensor;
    import processing.core.PApplet;
     
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
     
    import java.net.InetAddress;
    import java.net.Socket;
     
    import java.lang.*;
    import java.io.*;
     
     
    public class Sensorkipp_pde extends PApplet {
     
        private static class Client {
     
            private Socket socket = null;
            private BufferedReader reader = null;
            private BufferedWriter writer = null;
     
            public Client(InetAddress address, int port) throws IOException {
     
                socket = new Socket(address, port);
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            }
     
            public void send(String msg) throws IOException
            {
                writer.write(msg, 0, msg.length());
                writer.flush();
            }
     
            public String recv() throws IOException {
     
                return reader.readLine();
            }
     
            public static void main(String[] args) {
     
                try {
                    InetAddress host = InetAddress.getByName("192.168.42.1");
                    Client client = new Client(host, 9000);
     
                    client.send("Verbindung zum Server steht!!!!");
                    String response = client.recv();
                    System.out.println("Client received: " + response);
                }
                catch (IOException e) {
                    System.out.println("Caught Exception: " + e.toString());
                }
            }
        }
     
    KetaiSensor sensor;
     
    //In folgenden Variablen werden die X-, Y- und Z-Beschleunigungswerte eingetragen
    float beschleunigungX, beschleunigungY, beschleunigungZ;
     
    public void setup()
    {
     
      sensor=new KetaiSensor(this);
      sensor.start();
      orientation (LANDSCAPE);
     
      textAlign(CENTER, CENTER);
      textSize(36);
    }
     
    public void draw ()
    {
        //Variablen für die X- und Y-Positionen des Kreises
      int x=0, y=0;
        //Variablen für Kippwinkel der X- oder Y-Achse
      int kippWinkelY=0, kippWinkelX=0;
      background (0, 255, 0);
     
        //Fadenkreuz in der Mitte des Anzeigegerätes
      strokeWeight(2);
      line(300, 20, 300, 530);
      line(20, 300, 570, 300);
     
      //Berechnung der trigometrischen Berechnung der Kippwinkel In Grad (Int)
      // Wandelt von float in Int
      //Nach Rechts kippen ergibt ein positiven Winkel
      kippWinkelX= (int) (atan (beschleunigungY/beschleunigungZ) *180/PI);
      //Zum Benutzer kippen ergibt ein positiven Winkel
      kippWinkelY= (int) (atan (beschleunigungX/beschleunigungZ) *180/PI);
     
      //Umrechnung der Kippwinkel in dsa xy-Koordinatenpositionen
      x=(int)map(kippWinkelX, -90, 90, 0, 600);
      y=(int)map(kippWinkelY, -90, 90, 0, 600);
     
      // Kreis wird an gewünschter Koordinatenposition gezeichnet
      fill (255, 255, 255);
      ellipse(x, y, 50, 50);
      fill (0, 0 , 0);
     
      //Gibt Kippwinkel als Text aus
      text("Kippwinkel (\u00b0) : \n" + //(3)
      "X-Achse: " + kippWinkelX + "\n" +
      "Y-Achse: " + kippWinkelY + "\n", width/2, height/2);
    }
     
    public void onAccelerometerEvent (float x, float y, float z)
    {
      //Folgende Methoden werden automatisch aufgerufen bei Sensorwertänderung
      beschleunigungX=-x;
      beschleunigungY=-y;
      beschleunigungZ=-z;
    }
     
     
      public int sketchWidth() { return 600; }
      public int sketchHeight() { return 600; }
    }
```

Ich fände es natürlich super, wenn ein paar von euch euer Wissen mit mir teilen könnten.


----------



## evocrossfireX (9. Sep 2014)

Alex1820 hat gesagt.:


> Hallo Zusammen,
> 
> anbei präsentiere ich mein aktuelles Projekt und dessen Problematik. Gründe für die Problematiken werden wahrscheinlich sein, dass ich in diesen Gebieten ein blutiger Anfänger bin und hoffe mir daher etwas erfahrene Unterstützung.
> 
> ...



 Leider kann ich nicht deinen Server Code sehen.
Auf den ersten Blick sehen ich keinen gravierenden Fehler im Client.


----------



## dzim (9. Sep 2014)

Vielleicht liegt es daran, dass ich Processing (wenn ich es richtig verstehe ein AWT-/Swing(?)-Framework) nicht kenne, aber ich sehe an keiner Stelle irgendwie einen Bezug zu Android.

Daher für mich zum Verständnis:

- Die App läuft in der Form so auch unter Android?

- Ist das Processing-Framework so toll, dass du keine "reine" Android-App schreiben möchtest?

- Sind alle Berechtigungen (z.B. "android.permission.INTERNET") vorhanden (andererseits würdest du dann wahrscheinlich eine Exception auf der Konsole sehen)?

- Läuft der Netzwerk-Teil (Sockets, etc.) auf einem separaten Thread (allerdings würde auch das (bei höheren API-Level, wie ab der 14) eine Exception werfen)?

- Die #main-Methode und Android sind auch eher inkompatibel...

- Die Main Activity "Sensorkipp_pde" ist auch im Manifest eingetragen?

```
<activity
            android:name="processing.test.sensorkipp_pde.Sensorkipp_pde"
            android:finishOnTaskLaunch="true"
            android:icon="@drawable/cnlab"
            android:label="@string/speedTest"
            android:launchMode="singleTop" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
```

- Anmerkung: Während ein Unterstrich in Packagenamen noch ok, wenn auch unüblich, ist, haben sie in Klassen-/Methodennamen lt. "Code-Konvention" nichts zu suchen. Ist ja kein C.

- Statt mit Sockets könntest du auch einen Post über den in Android bis Version 4.4.4 eingebauten Apache HttpClient senden (Die Daten als XML/JSON/YAML/ProtoBuf/CSV/... im Content). Dadurch müsstest du hier zwar mehr Arbeit in den Server stecken, aber dann ist es etwas mehr "High-Level"... Nur so als Idee.


----------



## Alex1820 (9. Sep 2014)

Ich danke schon einmal vielmals, dass ihr mir bei deim Projekt etwas über die Schulter schaut.  

Also die App läuft auf meinem Tablet/Smartphone oder auch bedingt im Emulator, da sie da keine Sensordaten erhält. Ich habe am Anfang Processing gewählt, weil es einfacher  zu erlernen war. Ich habe dann später für die Verbindung das Projekt nach Android Studio exportiert. Was die Berechtigungen angeht, bin ich mir nicht ganz sicher. Allerdings handelt es sich bei dem Projekt um eine Wifi Punkt zu Punkt Verbindung, weshalb ich das Internet nicht benötige.  Auch die Main Activity ist im Manifest eingetragen. Der Strich im Packagenamen ist durch die Exportierung entstanden, hat Android Studio also selber so erstellt. 

Ich habe nun an dem Projekt selber auch weitergearbeitet. Ich habe mich nun vorerst für eine Version entschieden, bei der die Variablen in eine Datei geschrieben werden sollen und diese dann übertragen wird. Leider funktioniert diese Version auch noch nicht richtig. 

Anbei poste ich mal den vollständigen Code auch mit dem Raspberry Server

App:
sensorkipp_pde.java

```
package processing.test.sensorkipp_pde;

import processing.core.*; 
import processing.data.*; 
import processing.event.*; 
import processing.opengl.*; 

import ketai.net.nfc.record.*; 
import ketai.camera.*; 
import ketai.net.*; 
import ketai.ui.*; 
import ketai.cv.facedetector.*; 
import ketai.sensors.*; 
import ketai.net.nfc.*; 
import ketai.net.wifidirect.*; 
import ketai.data.*; 
import ketai.net.bluetooth.*; 
import ketai.sensors.*; 

import java.util.HashMap; 
import java.util.ArrayList; 
import java.io.File; 
import java.io.BufferedReader; 
import java.io.PrintWriter; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.io.IOException;
import java.net.Socket;
import java.net.*;
import java.io.*;
import java.net.UnknownHostException;
import java.io.InputStreamReader;
import java.net.Socket;

public class Sensorkipp_pde extends PApplet {

    public static void main(String[] args)
    {
        Socket socket;
        try
        {
            socket = new Socket("192.168.42.1", 3033);
            if(!socket.isConnected())
                System.out.println("Socket Connection Not established");
            else
                System.out.println("Socket Connection established : "+socket.getInetAddress());

            File myfile = new File("<SOURCE PATH>/position.txt");       //local file path.


            if(!myfile.exists())
                System.out.println("File Not Existing.");
            else
                System.out.println("File Existing.");

            byte[] byteArray = new byte[1024];

            FileInputStream fis = new FileInputStream(myfile);
            BufferedInputStream bis = new BufferedInputStream(fis);
            OutputStream os = socket.getOutputStream();
            int trxBytes =0;
            while((trxBytes = bis.read(byteArray, 0, byteArray.length)) !=-1)
            {
                os.write(byteArray, 0, byteArray.length);
                System.out.println("Transfering bytes : "+trxBytes );
            }
            os.flush();
            bis.close();
            socket.close();

            System.out.println("File Transfered...");
        }
        catch(Exception e)
        {
            System.out.println("Client Exception : "+e.getMessage());
        }
    }

//In folgenden Variablen werden die X-, Y- und Z-Beschleunigungswerte eingetragen

KetaiSensor sensor;
float beschleunigungX, beschleunigungY, beschleunigungZ;

public void setup()
{
 
  sensor=new KetaiSensor(this);
  sensor.start();
  orientation (LANDSCAPE);

  textAlign(CENTER, CENTER);
  textSize(36);
}

public void draw ()
{
    //Variablen für die X- und Y-Positionen des Kreises
  int x=0, y=0;
    //Variablen für Kippwinkel der X- oder Y-Achse
  int kippWinkelY=0, kippWinkelX=0;
  background (0, 255, 0);

    //Fadenkreuz in der Mitte des Anzeigegerätes
  strokeWeight(2);
  line(300, 20, 300, 530);
  line(20, 300, 570, 300);


    //Berechnung der trigometrischen Berechnung der Kippwinkel In Grad (Int)
    // Wandelt von float in Int
    //Nach Rechts kippen ergibt ein positiven Winkel
  kippWinkelX= (int) (atan (beschleunigungY/beschleunigungZ) *180/PI);
    //Zum Benutzer kippen ergibt ein positiven Winkel
  kippWinkelY= (int) (atan (beschleunigungX/beschleunigungZ) *180/PI);

    //Umrechnung der Kippwinkel in dsa xy-Koordinatenpositionen
  x=(int)map(kippWinkelX, -90, 90, 0, 600);
  y=(int)map(kippWinkelY, -90, 90, 0, 600);

    // Kreis wird an gewünschter Koordinatenposition gezeichnet
  fill (255, 255, 255);
  ellipse(x, y, 50, 50);
  fill (0, 0 , 0);

    //Gibt Kippwinkel als Text aus
  text("Kippwinkel (\u00b0) : \n" + //(3)
  "X-Achse: " + kippWinkelX + "\n" +
  "Y-Achse: " + kippWinkelY + "\n", width/2, height/2);
}

public void onAccelerometerEvent (float x, float y, float z)
{
    //Folgende Methoden werden automatisch aufgerufen
    // Wird in folgenden Variablen gespeichert
  beschleunigungX=-x;
  beschleunigungY=-y;
  beschleunigungZ=-z;
}

public void saveConf() {
 String[] data = new String[3];

// Zahlen werden in Strings umgewandelt
data[0] = "" + beschleunigungX;
data[1] = "" + beschleunigungY;
data[2] = "" + beschleunigungZ;

saveStrings("position.txt", data);
}

public void loadConf() {
 String[] data = loadStrings("position.txt");

// Strings in Zahlen umwandeln
beschleunigungX = PApplet.parseInt(data[0]);
beschleunigungY = PApplet.parseInt(data[1]);
beschleunigungZ = PApplet.parseInt(data[2]);
}

    //Testmethode: Mit S wird Wert gespeichert & mit i wieder aufgefrufen
/*public void keyPressed() {
  if (key == 's') {
    saveConf();
  }
  if (key == 'i') {
    loadConf();
  }
}*/

  public int sketchWidth() { return 600; }
  public int sketchHeight() { return 600; }
}
```



Manifest.xml


```
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="processing.test.sensorkipp_pde">
    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="16"/>
    <application android:debuggable="true" android:icon="@drawable/icon" android:label="Sensorkipp_pde">
        <activity android:name=".Sensorkipp_pde">
            <intent-filter><action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>
```

Python-Server-Code
server.py


```
#!/usr/bin/env python
# server.py

import socket, time, string, sys, urlparse
from threading import *

#------------------------------------------------------------------------

class StreamHandler ( Thread ):

    def __init__( this ):
        Thread.__init__( this )

    def run(this):
        this.process()

    def bindmsock( this ):
        this.msock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        this.msock.bind(('', 3033))
        this.msock.listen(1)
        print '[Media] Listening on port 3033'

    def acceptmsock( this ):
        this.mconn, this.maddr = this.msock.accept()
        print '[Media] Got connection from', this.maddr
    
    def bindcsock( this ):
        this.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        this.csock.bind(('', 3034))
        this.csock.listen(1)
        print '[Control] Listening on port 3034'

    def acceptcsock( this ):
        this.cconn, this.maddr = this.csock.accept()
        print '[Control] Got connection from', this.maddr
        
        while 1:
            data = this.cconn.recv(1024)
            if not data: break
            if data[0:4] == "SEND": this.filename = data[5:]
            print '[Control] Getting ready to receive "%s"' % this.filename
            break

    def transfer( this ):
        print '[Media] Starting media transfer for "%s"' % this.filename

        f = open(this.filename,"wb")
        while 1:
            data = this.mconn.recv(1024)
            if not data: break
            f.write(data)
        f.close()

        print '[Media] Got "%s"' % this.filename
        print '[Media] Closing media transfer for "%s"' % this.filename
    
    def close( this ):
        this.cconn.close()
        this.csock.close()
        this.mconn.close()
        this.msock.close()

    def process( this ):
        while 1:
            this.bindcsock()
            this.acceptcsock()
            this.bindmsock()
            this.acceptmsock()
            this.transfer()
            this.close()

#------------------------------------------------------------------------

s = StreamHandler()
s.start()
```


----------



## Alex1820 (10. Sep 2014)

Der Python-Code war leider nicht der richtige. Hier habe ich nochmal meinen richtigen Server-Code zum Dateien-Empfang


```
import socket
import select
import Queue
from threading import Thread
from time import sleep
import sys
 
PORT=3033

class ProcessThread(Thread):
    def __init__(self):
        super(ProcessThread, self).__init__()
        self.running = True
        self.q = Queue.Queue()
 
    def add(self, data):
        self.q.put(data)
 
    def stop(self):
        self.running = False
 
    def run(self):
        q = self.q
        while self.running:
            try:
                # block for 1 second only:
                value = q.get(block=True, timeout=1)
                process(value)
            except Queue.Empty:
                sys.stdout.write('.')
                sys.stdout.flush()
        #
        if not q.empty():
            print "Elements left in the queue:"
            while not q.empty():
                print q.get()
 
t = ProcessThread()
t.start()
 

 
def main():
    s = socket.socket()         
    host = socket.gethostname() 
    port = cfg.PORT                
    s.bind((host, port))       
    print "Listening on port {p}...".format(p=port)
 
    s.listen(5)                 # Now wait for client connection.
    while True:
        try:
            client, addr = s.accept()
            ready = select.select([client,],[], [],2)
            if ready[0]:
                data = client.recv(4096)
                #print data
                t.add(data)
        except KeyboardInterrupt:
            print
            print "Stop."
            break
        except socket.error, msg:
            print "Socket error! %s" % msg
            break
    #
    cleanup()
 
def cleanup():
    t.stop()
    t.join()
 

 
if __name__ == "__main__":
    main()
```


----------



## dzim (10. Sep 2014)

Das Problem ist, dass unabhängig vom Namen der Permission, diese notwendig ist, um Sockets zu öffnen:

Manifest.permission | Android Developers
Zitat: "Allows applications to open network sockets."

Du musst das also in dein Manifest einfügen. Das die Anwendung am Ende nur Wifi benützt, spielt erst einmal keine Rolle. Ausserdem müssten - je nach Gerät - alle Netzwerkoperationen auf einem anderen Thread durchgeführt werden.

Ich habe es noch einmal kurz überprüft: Die Sensoren scheinen sonst aber keine spezielle Berechtigung zu benötigen.

Was deinen Server angeht: Ich bin leider im Moment nicht mehr fit genug in Python, um zu bewerten, ob der korrekt ist, oder nicht...


----------



## Alex1820 (12. Sep 2014)

Danke dzim! Alleine wäre ich darauf nicht gekommen. Ich habe nun die Berechtigungen hinzugefügt. Laufen tut es zwar immer noch nicht, aber ich denke ich näher mich langsam der Lösung.


----------

