(Teil 1) Netzwerkverbindung zwischen Windows Phone 7 und Netduino Plus

WP7_und_Netduino_01
Netduino Plus und Windows Phone 7 Mobiltelefon

Eigentlich hatte ich vor, schon früher einen neuen Post zu schreiben. Aber wie so oft fehlt mir leider die Zeit. Und da ich versuche, weniger Kaffee zu trinken, werden meine Abende auch immer kürzer.

Heute zeige ich ein einfaches und kurzes Beispiel, wie man mit dem Windows Phone 7 und dem Netduino eine Netzwerkverbindung herstellen kann. Der Quellcode ist wieder auf das Wesentliche gekürzt, um möglichst nur die Funktionsweise von notwendigen Klassen zu zeigen.
Das Ziel des Beispiel wird sein, einen Servo anzusteuern. Am besten fangen wir mit der Anwendung auf dem Windows Phone 7 an. Hierzu benötigen wir einen “Slider” um einen Stellwert zum Netduino übermitteln zu können sowie zwei “Textboxen” um später die Zieladresse und den Port zu bestimmen. Zuletzt einen Button, um die Verbindung herzustellen. Mit “Textblock” können Status und der eingestellte Wert angezeigt werden. (Eine fertige Beispiel-Solution habe ich am Ende des Posts eingefügt)

image
So könnte die Anwendung auf der App aussehen.


Nun zum interessanten Teil der App. Um eine Netzwerkverbindung herzustellen, legen wir eine neue Klasse an.

   1: /// <summary>
   2: /// Stellt eine Netzwerkverbindung her
   3: /// </summary>
   4: public class NetworkRemote
   5: {
   6:     /// <summary>
   7:     /// Mit Hilfe der Socket Klasse 
   8:     /// richten wir den Client für diese Anwendung ein.
   9:     /// </summary>
  10:     private Socket _Socket = null;
  11:     /// <summary>
  12:     /// Wie lange auf die Antwort vom Server gewartet wird (in Milisekunden)
  13:     /// </summary>
  14:     private int _Timeout = 5000;
  15:     /// <summary>
  16:     /// Die Thread Klasse wird verwendet, damit die Oberfläche der Anwendung
  17:     /// nicht anhält, wenn z.B. gesendet wird.
  18:     /// </summary>
  19:     private Thread _ThreadSend;
  20:     /// <summary>
  21:     /// Wird verwendet, um die Rückantwort zum Thread zu ermöglichen.
  22:     /// </summary>
  23:     private ManualResetEvent _ClientDone = new ManualResetEvent(false);
  24:  
  25:     /// <summary>
  26:     /// Stellt zu der Host Adresse und Port Nummer eine TCP Verbindung her
  27:     /// </summary>
  28:     /// <param name="hostAddress"></param>
  29:     /// <param name="port"></param>
  30:     /// <returns></returns>
  31:     public string Connect(string hostAddress, int port)
  32:     {
  33:         // Rückanwort, ob nun die Verbindung hergestellt werden konnte.
  34:         string result = string.Empty;
  35:  
  36:         // Ziel Adresse und Port festlegen.
  37:         DnsEndPoint dnsEndPoint = new DnsEndPoint(hostAddress, port);
  38:  
  39:         // Die Socket Klasse initialisieren
  40:         _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  41:  
  42:         // Asynchrone Socket Event
  43:         SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
  44:  
  45:         // Endpunkt an Event übergeben.
  46:         socketEventArg.RemoteEndPoint = dnsEndPoint;
  47:  
  48:         // Event zuweisen
  49:         socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
  50:         {
  51:             // Ruft das Ergebnis der Anfrage
  52:             result = e.SocketError.ToString();
  53:  
  54:             // Signal, dass der Antrag vollständig ist
  55:             _ClientDone.Set();
  56:         });
  57:  
  58:         // Legt den Zustand des Ereignisses auf nicht signalisiert, so der Thread blockiert ist.
  59:         _ClientDone.Reset();
  60:  
  61:         // Verbindung herstellen, die asynchron verläuft
  62:         _Socket.ConnectAsync(socketEventArg);
  63:  
  64:         // Blockiert den Thread in Millisekunden.
  65:         _ClientDone.WaitOne(_Timeout);
  66:  
  67:         return result;
  68:     }
  69:     /// <summary>
  70:     /// Daten senden, nachdem eine Verbindung hergestellt wurde.
  71:     /// Das Senden wird in einem Thread ausgeführt
  72:     /// </summary>
  73:     /// <param name="data">Eingabe der Daten</param>
  74:     /// <returns>Ergbnis der gesendeten Daten</returns>
  75:     public void Send(byte[] data)
  76:     {
  77:         // Nur einen neuen Thread starten, wenn dieser nicht aktiv ist.
  78:         if (_ThreadSend == null || !_ThreadSend.IsAlive)
  79:         {
  80:             _ThreadSend = new Thread(new ParameterizedThreadStart(SendInThread));
  81:             _ThreadSend.Start(data);
  82:         }
  83:     }
  84:     /// <summary>
  85:     /// Der Vorgang sollte in einem Thread ausgeführt werden. Das verhindert,
  86:     /// dass die Oberfläche stehenbleibt
  87:     /// </summary>
  88:     /// <param name="obj"></param>
  89:     private void SendInThread(object obj)
  90:     {
  91:         // Status Meldung hinterlegen.
  92:         string response = "Operation Timeout";
  93:  
  94:         // Asynchrone Socket Event
  95:         SocketAsyncEventArgs socketEvent = new SocketAsyncEventArgs();
  96:  
  97:         // Endpunkt an Event übergeben.
  98:         socketEvent.RemoteEndPoint = _Socket.RemoteEndPoint;
  99:  
 100:         // Zuordnung auf Null setzen 
 101:         socketEvent.UserToken = null;
 102:  
 103:         // Event zuweisen
 104:         socketEvent.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
 105:         {
 106:             response = e.SocketError.ToString();
 107:             _ClientDone.Set();
 108:         });
 109:  
 110:         // Übergabe der zu sendenden Daten.
 111:         socketEvent.SetBuffer((byte[])obj, 0, ((byte[])obj).Length);
 112:  
 113:         // Legt den Status des Ereignisses für Kein Signal, verursacht Thread Blockade
 114:         _ClientDone.Reset();
 115:  
 116:         // Stellt eine asynchrone Sendungsantwort ausserhalb des Socket
 117:         _Socket.SendToAsync(socketEvent);
 118:  
 119:         // Blockiert den Thread in Millisekunden.
 120:         _ClientDone.WaitOne(_Timeout); 
 121:  
 122:         // Event senden.
 123:         MessageEvent(response);
 124:     }
 125:     /// <summary>
 126:     /// schließt die Socket Verbindung
 127:     /// </summary>
 128:     public void Close()
 129:     {
 130:         if (_Socket != null)
 131:         {
 132:             _Socket.Close();
 133:         }
 134:     }
 135:     /// <summary>
 136:     /// Ruft ab, ob der Thread für Senden noch aktiv ist
 137:     /// </summary>
 138:     public bool IsSending
 139:     {
 140:         get
 141:         {
 142:             if (_ThreadSend.ThreadState == ThreadState.Running)
 143:             {
 144:                 return true;
 145:             }
 146:  
 147:             return false;
 148:         }
 149:     }
 150:     /// <summary>
 151:     /// Ruft ab, ob die Netzwerkverbindung besteht.
 152:     /// </summary>
 153:     public bool IsConnected
 154:     {
 155:         get
 156:         {
 157:             try
 158:             {
 159:                 return _Socket.Connected;
 160:             }
 161:             catch
 162:             {
 163:                 return false;
 164:             }
 165:         }
 166:     }
 167:  
 168:     public delegate void NetwokrMessageHandler(object sender, NetworkMessageEventArgs e);
 169:     public event NetwokrMessageHandler NetworkMessage;
 170:     /// <summary>
 171:     /// Event auslösen; durch Ausführen der Methode
 172:     /// wird an die obere Klasse ein Ereignis gesendet, 
 173:     /// um z.B. einen Status wiederzugeben.
 174:     /// </summary>
 175:     /// <param name="message"></param>
 176:     public virtual void MessageEvent(string message)
 177:     {
 178:         if (NetworkMessage != null)
 179:         {
 180:             NetworkMessage(this, new NetworkMessageEventArgs(message));
 181:         }
 182:     }
 183: }



Für Rückmeldungen wie Status, wird ein Event Objekt benötigt, damit ich als Anwender weiss, ob die Verbindung erfolgreich war.


   1: public class NetworkMessageEventArgs
   2: {
   3:     public string Message { get; private set; }
   4:  
   5:     public NetworkMessageEventArgs(string message)
   6:     {
   7:         Message = message;
   8:     }
   9: }


Für das Beispiel ist eigentlich ein Protokoll noch zu viel, soll jedoch zeigen, das für die Übertragung weitere Werte hinzugefügt werden könnten.




   1: /// <summary>
   2: /// Ein Objekt das als Protokol für die Übertragung verwendet wird.
   3: /// Der Inhalt kann beliebig erweitert werden. 
   4: /// Im Beispiel wird zunächst ein Wert übermittelt.
   5: /// </summary>
   6: public struct NetworkProtocolRemote
   7: {
   8:     /// <summary>
   9:     /// Die Variable zum festlegen des Stellwertes.
  10:     /// </summary>
  11:     private int _ServoValue;
  12:     /// <summary>
  13:     /// Initialisiert das Protokol mit Variabeln.
  14:     /// </summary>
  15:     /// <param name="servoValue"></param>
  16:     public NetworkProtocolRemote(int servoValue)
  17:     {
  18:         _ServoValue = servoValue;
  19:     }
  20:     /// <summary>
  21:     /// Empfangenes Byte Array wird eingelesen und befüllt die Variablen.
  22:     /// </summary>
  23:     /// <param name="receive"></param>
  24:     public NetworkProtocolRemote(byte[] receive)
  25:     {
  26:         _ServoValue = 90;
  27:  
  28:         string protocol = Encoding.UTF8.GetString(receive, 0, receive.Length);
  29:         SetProperties(protocol);
  30:     }
  31:     /// <summary>
  32:     /// Der Inhalt des Empfangenen Protokols wird in die Variablen festlegt.
  33:     /// </summary>
  34:     /// <param name="protocol"></param>
  35:     private void SetProperties(string protocol)
  36:     {
  37:         // Das Protokol kann um weitere Variablen erweitert werden,
  38:         // daher habe ich hier eine Schleife eingesetzt, 
  39:         // um die Variablen festzulegen.
  40:  
  41:         foreach (string item in protocol.Split(';'))
  42:         {
  43:             string[] sa = item.Split(':');
  44:  
  45:             switch (sa[0])
  46:             {
  47:                 case ("ServoValue"):
  48:                     {
  49:                         _ServoValue = Convert.ToInt16(sa[1]);
  50:                         break;
  51:                     }
  52:                 default:
  53:                     {
  54:                         break;
  55:                     }
  56:             }
  57:         }
  58:     }
  59:     /// <summary>
  60:     /// Gibt den Inhalt des Objektes als Byte Array zurück
  61:     /// </summary>
  62:     /// <returns></returns>
  63:     public byte[] GetRemoteToByte()
  64:     {
  65:         string content = string.Empty;
  66:  
  67:         content += "ServoValue:" + _ServoValue + ";";
  68:  
  69:         return Encoding.UTF8.GetBytes(content);
  70:     }
  71:     /// <summary>
  72:     /// Ruft den Wert des Servos ab oder legt diesen fest.
  73:     /// </summary>
  74:     public int ServoValue
  75:     {
  76:         set { _ServoValue = value; }
  77:         get { return _ServoValue; }
  78:     }
  79: }

Das Wesentliche zeigt die Klasse im ersten Code Schnipsel (natürlich lässt sich dies noch kürzer schreiben, aber ich wollte nicht zu viel auslassen).

Im zweiten Teil des Posts geht es mit dem Netduino weiter.
Zweiter Teil

Die fertige Windows Phone 7 App Solution für Visual Studio 2010:
WP7 App Solution - Download

Eine Windows Desktop Anwendung zum testen:
Desktop Test Anwendung - Download

Kommentare

Beliebte Posts aus diesem Blog

Arduino Control (Teil 5) - PWM Signal einlesen

RC Fahrtenregler für Lego Kettenfahrzeug

Angular auf dem Raspberry Pi