Freigeben über


Embedded-Programmierung

Verbundene Geräte und .NET Micro Framework

Colin Miller

Beispielcode herunterladen.

Anwendungen mit verbundenen Geräten sind immer weiter verbreitet. Es wird sogar angenommen, dass das „Internet der Dinge“ (über das Internet miteinander verbundene Geräte) schon größer geworden ist als das World Wide Web, was die Endpunkte betrifft, und dass es in den nächsten Jahren noch deutlich wachsen wird.

In naher Zukunft werden wir, statt mit identifizierbaren Computern, mit weitaus intelligenteren Geräten kommunizieren. Sehen Sie sich einfach zuhause um. Zu den Dingen, die sinnvollerweise miteinander verbunden werden könnten, zählen Ihre Geräte (Energieverwaltung, Softwareupdates und Wartung), Ihr Auto (Koordination der Aufladung des neuen Elektrofahrzeugs am Netz, automatisches Testen, Wartung und Softwareupdates), Ihr Bewässerungssystem (Planung auf Basis der Wettervorhersage sowie Wasserverwaltung), Ihre Haustiere (Bestimmen ihres Standorts und Konfigurieren unsichtbarer Zäune), Ihr Thermostat (Fernbedienung) und vieles mehr.

Diese Geräte werden miteinander, mit intelligenten Controllern, mit dem Router und mit der Cloud verbunden. Was bedeutet das für Entwickler von Microsoft .NET Framework? Momentan können .NET-Entwickler für alle Bestandteile des Systems, zu denen kleinere Geräte eine Verbindung herstellen, Anwendungen entwickeln. Aber mit dem .NET Micro Framework können sie das gesamte System entwickeln, bis hin zu diesen Geräten.

Das .NET Micro Framework stellt eine Implementierung des .NET Frameworks für die kleinstmöglichen Geräte dar und bietet Erweiterungen für die Anforderungen von Embedded-Programmierung. Es wird direkt auf der Hardware ausgeführt und benötigt kein Betriebssystem, was den Speicherbedarf minimiert. Allgemeine Informationen dazu finden Sie unter microsoft.com/netmf und auf der Website der Open Source Community des Projekts: netmf.com.

Vor einigen Monaten habe ich eine Reihe von Artikeln im .NET Micro Framework-Blog verfasst (blogs.msdn.com/b/netmfteam), in denen ich die Erstellung eines solchen kleinen Geräts am Beispiel eines Fahrradcomputers detailliert beschrieben habe. Verwendet habe ich lediglich .NET Micro Framework, Visual Studio und ein Mindestmaß an Elektronik. Damit wollte ich veranschaulichen, wie .NET Framework-Entwickler umfassende Anwendungen auf kleinen Geräten erstellen können. (Den ersten Artikel dieser Reihe finden Sie unter: tinyurl.com/2dpy6rx.)  Abbildung 1 zeigt den eigentlichen Computer. Ich habe den Fahrradcomputer gewählt, weil sich damit jeder auskennt. Die Anwendung umfasst eine bewegungsgesteuerte Benutzeroberfläche, unterstützt zahlreiche Sensoren und ermöglicht Dinge wie das Verwalten der Stromversorgung. 

image: The NETMF Bicycle Computer

Abbildung 1 Der NETMF-Fahrradcomputer

In dem Blog wird die Implementierung der einzelnen Features diskutiert, und der Code für das Projekt ist über CodePlex verfügbar unter: netmfbikecomputer.codeplex.com/. Das Beste kommt allerdings zum Schluss: in diesem Artikel verbinde ich das Gerät über WiFi mit einem auf Microsoft Windows Azure gehosteten Webdienst. Hier ist das Szenario: Sie kommen vom Fahrradfahren zurück und haben Ihr Fahrrad in die Garage gebracht. Auf dem Computer sind die während der Fahrt gesammelten Daten gespeichert: Entfernung, Geschwindigkeit, Trittfrequenz, Steigungen, Zeit usw. Sie wechseln zur Datenansicht und drücken die Schaltfläche zum Hochladen. Die Daten werden in die Cloud hochgeladen, wo sie mit den Daten der anderen Fahrten aggregiert und für Freunde freigegeben werden.

In diesem Artikel konzentriere ich mich darauf, wie die Verbindung hergestellt und die Daten hochgeladen werden, statt auf die Art des Cloud-Diensts einzugehen. Sie können sich beispielsweise auf den Websites bikejournal.com und cyclistats.com anschauen, welche Möglichkeiten es gibt, Ihre Fortschritte zu dokumentieren und mit Freunden kleine Wettkämpfe auszuführen. Ich werde erläutern, wie einfach es ist, diese Verbindung zu erstellen.

Zunächst ein paar Hintergrundinformationen

Das Webdienstmodell eignet sich gut für die Verbindung von Geräten, da es die verschiedensten Interaktionen zwischen Geräten und Diensten unterstützt, die zum jetzigen Zeitpunkt möglicherweise noch gar nicht alle bekannt sind. Geräteseitig verwenden wir einen Teil der vollständigen Webserviceinfrastruktur namens Devices Profile for Web Services (DPWS), worüber Sie sich unter en.wikipedia.org/wiki/Devices_Profile_for_Web_Services genauer informieren können. DPWS wird als universelles Plug & Play (UPNP) für vernetzte Geräte beschrieben. Im .NET Micro Framework unterstützt DPWS die Schnittstellen WS-Addressing, WS-Discovery, WS-MetaDataExchange und WS-Eventing und basiert auf den Technologien SOAP, XML, HTTP, MTOM und Base64-Codierung.

Mit DPWS können Sie die Verbindung zu Geräten herstellen, die als Clients (fremde Dienste nutzen), Server (Dienste für andere bereitstellen) oder beides fungieren. Es ist verhandelbar, was Sie anbieten und was Sie über die Metadaten nutzen, und Sie können Benachrichtigungen über Änderungen in anderen Entitäten veröffentlichen bzw. abonnieren. In Abbildung 2 wird der DPWS-Stapel angezeigt.

image: The DPWS Stack

Abbildung 2 Der DPWS-Stapel

Die Implementierung im .NET Micro Framework unterstützt DPWS Version 1.0, die kompatibel ist mit Windows 7, und DPWS Version 1.1, die kompatibel ist mit Windows Communication Foundation (WCF) 4. Sie können angeben, welche Bindung für die Verbindung verwendet werden soll: zum Beispiel SOAP über HTTP (ws2007HttpBinding) oder eine benutzerdefinierte Bindung, die Sie unterstützen möchten.

Unsere Anwendung für den Fahrradcomputer ist wirklich ziemlich simpel – wir laden einfach Daten in die Cloud hoch. Dabei kann DPWS so viel mehr. Angenommen, mein Energieversorgungsunternehmen installiert einen intelligenten Zählerdienst, der einschränken kann, welche Ressourcen ich zu einer bestimmten Zeit verwenden kann. Dazu setze ich einen lokalen Energieverwaltungsdienst ein, mit dem ich die Nutzung der eingeschränkten Ressourcen steuern kann. Ich kann Prioritäten und eine Heuristik festlegen, auf deren Grundlage das System den Verbrauch einschränkt, so könnte heißes Wasser zum Duschen eine hohe Priorität bekommen.

Jetzt kaufe ich eine neue Spülmaschine. Ich nehme sie zuhause in Betrieb. Im Hintergrund findet das Gerät das lokale Netzwerk und „erkennt“ den Verwaltungsdienst. Es informiert den Dienst über seinen Stromverbrauch in den verschiedenen Phasen und über Regeln zur möglichen Verwendung. Während später die Spülmaschine läuft, springe ich unter die Dusche, und der Warmwasserbereiter springt an. Nachdem ich die Spülmaschine eingeschaltet habe, möchte ich nicht, dass sie einfach ausgeschaltet wird und die Essensreste auf den Tellern verkrusten. Um den Gesamtverbrauch zu verringern, weist der Verwaltungsdienst die Spülmaschine an, das Geschirr alle 15 Minuten abzuspülen, damit es nicht austrocknet. Wenn ich mit dem Duschen fertig bin, soll das Spülprogramm an der Stelle fortgesetzt werden, an der es zuvor gestoppt wurde. Wie Sie sehen, kann dieses Szenario beliebig viele Endpunkte mit den in DPWS definierten Funktionen unterstützen.

Genug Hintergrund – packen wir's an

Das Wi-Fi-Radio ist sehr einfach zu konfigurieren. Dabei gibt es zwei Möglichkeiten, entweder verwenden Sie das Dienstprogramm „MFDeploy.exe“ oder eine befehlsorientierte Benutzerschnittstelle, die vom GHI-SDK (von GHI Electronics) unterstützt wird. Beginnen wir mit „MFDeploy.exe“, einem Tool, das mit dem .NET Micro Framework ausgeliefert wird und im Abschnitt „Tools“ der SDK-Installation gespeichert ist. Im Dialogfeld „Target | Configuration | Network Configuration“ (Ziel | Konfiguration | Netzwerkkonfiguration) (siehe Abbildung 3) aktiviere ich „DHCP“, wähle den Sicherheitsmechanismus aus und gebe die Passphrase und andere Konfigurationsaspekte für mein Heimnetzwerk ein.

image: The MFDeploy Network Configuration Dialog

Abbildung 3 MFDeploy - Dialogfeld zur Netzwerkkonfiguration

Die Felder für Gateway und DNS der Netzwerkeinstellungen werden von DHCP ausgefüllt. Diese Informationen werden für die verwaltete Anwendung über den NetworkInterface-Typ und dessen untergeordnete Wireless80211-Schnittstelle verfügbar gemacht. Die Daten werden beim Senden und Empfangen von Bytes implizit vom HTTP-Stapel verwendet, aber damit ein Proxy verwendet werden kann, was für Ihr Netzwerk erforderlich sein könnte, ist noch eine weitere Information nötig. Damit der HTTP-Stapel den Proxy richtig verwenden kann, sollten Sie dem Programm folgenden Code als weiteren Hinweis zur Verbindung hinzufügen:

WebRequest.DefaultWebProxy = 
  new WebProxy("<router IP Adress>");

Ist in Ihrem Netzwerk keine explizite Proxy-Rolle definiert, können Sie in der Regel standardmäßig die Gateway-Adresse verwenden. Mit der programmgesteuerten Schnittstelle von GHI verwenden Sie eine Ableitung des in Abbildung 4 gezeigten Codes.

Abbildung 4 Wi-Fi-Konfiguration mit der programmgesteuerten Schnittstelle von GHI

// -- Set up the network connection -- //

WiFi.Enable(SPI.SPI_module.SPI2, (Cpu.Pin)2, (Cpu.Pin)26);

NetworkInterface[] networks = NetworkInterface.GetAllNetworkInterfaces();
Wireless80211 WiFiSettings = null;

for (int index = 0; index < networks.Length; ++index)
{
  if (networks[index] is Wireless80211)
  {
    WiFiSettings = (Wireless80211)networks[index];
    Debug.Print("Found network: " + WiFiSettings.Ssid.ToString());
  }
}

WiFiSettings.Ssid = "yourSSID";
WiFiSettings.PassPhrase = "yourPassphrase";
WiFiSettings.Encryption = Wireless80211.EncryptionType.WPA;
Wireless80211.SaveConfiguration(
  new Wireless80211[] { WiFiSettings }, false);

_networkAvailabilityBlocking = new ManualResetEvent(false);

if (!WiFi.IsLinkConnected)
{
  _networkAvailabilityBlocking.Reset();
  while (!_networkAvailabilityBlocking.WaitOne(5000, false))
 {
    if (!WiFi.IsLinkConnected)
    {
      Debug.Print("Waiting for Network");
    }
    else
    break;
  }
}
Debug.Print("Enable DHCP");
try
{
  if (!WiFiSettings.IsDhcpEnabled)
    WiFiSettings.EnableDhcp(); // This function is blocking
  else
  {
    WiFiSettings.RenewDhcpLease(); // This function is blocking
  }
}
catch
{
  Debug.Print("DHCP Failed");
}

Im Beispiel aus Abbildung 4 wird davon ausgegangen, dass Sie WPA- oder WPA2-Sicherheit verwenden. Auch WEP wird unterstützt. Als ersten Schritt ermitteln Sie den SPI-Port und die Steuerungszeilen, die vom Radio verwendet werden. Diese Konfiguration stellt die Verbindungen für die Hardware dar, ein FEZ Cobra-Board von GHI. Sie müssen lediglich die WiFi-Einstellungen einrichten, die Konfiguration speichern und „EnableDHCP“ aufrufen. Beachten Sie, dass einiger dieser Aufrufe blockierend sind und der Vorgang daher etwas dauern kann. Der Benutzer sollte also wissen, was passiert. Außerdem gibt es bei dieser programmgesteuerten Schnittstelle keine Möglichkeit, die verfügbaren Netzwerke aufzulisten, um daraus auszuwählen. Für das Beispiel in Abbildung 4 habe ich die Netzwerkinformationen hartcodiert. 

Für eine kommerzielle Implementierung des Fahrradcomputers muss ich eine integrierte Wi-Fi-Konfigurationsoberfläche schreiben, auf der die im Dialogfeld in Abbildung 3 eingegebenen Informationen verfügbar gemacht werden, sowie eine Bildschirmtastatur für deren Eingabe. Vielleicht habe ich Zeit, darüber in einem anderen Blogbeitrag zu schreiben, bevor dieser Artikel veröffentlicht wird. Ich arbeite an einem Zeitdienstartikel, der zeigt, wie mit der Wi-Fi-Verbindung beim Start des Computers Datum und Uhrzeit abgerufen werden können, damit das Gerät nicht in Betrieb bleiben muss, um diese Daten zu erhalten, bzw. damit der Benutzer (ich) diese nicht beim Start eingeben muss.

Einrichten der Dienstverbindung

Nun bleibt nur noch die DPWS-Implementierung. Wie bereits erwähnt, konzentriere ich mich auf das Gerät. Der von mir verwendete Windows Azure-Dienst stellt eine einfache „Hello World“-Vorlage bereit. Diese nutze ich als Startpunkt und erweitere den Vertrag, indem ich die Felder hinzufüge, die ich zum Speichern benötige. Außerdem schreibe ich die UpLoad- und Get-Operationen. Auf diese Weise erstelle ich einen Dienst, der meine Daten annehmen sowie speichern und mir die zuletzt gespeicherten Daten zurückgeben kann. Natürlich ist ein kompletter Dienst viel aufwändiger, aber das wird Thema eines anderen Beitrags. Sehen wir uns den für diesen Dienst erstellten Vertrag kurz an. „ServiceContract“ umfasst die zwei Operationen und „DataContract“ die Felder (siehe Abbildung 5).

Abbildung 5 Der Dienstvertrag

[ServiceContract]
  public interface IBikeComputerService
  {
    [OperationContract]
    BikeComputerData GetLastComputerData();

    [OperationContract]
    void UploadBikeComputerData(BikeComputerData rideData);
  }


  // Use a data contract as illustrated in the sample below 
  // to add composite types to service operations.
  [DataContract]
  public class BikeComputerData
  {
    DateTime _Date;
    TimeSpan _StartTime;
    TimeSpan _TotalTime;
    TimeSpan _RidingTime;
    float    _Distance;
    float    _AverageSpeed;
    float    _AverageCadence;
    float    _AverageIncline;
    float    _AverageTemperature;
    bool     _TempIsCelcius;
            
    [DataMember]
    public DateTime Date…

    [DataMember]
    public TimeSpan StartTime…

    [DataMember]
    public TimeSpan TotalTime…

    [DataMember]
    public TimeSpan RidingTime…

    [DataMember]
    public float Distance…

    [DataMember]
    public float AverageSpeed…

    [DataMember]
    public float AverageCadence…

    [DataMember]
    public float AverageIncline…

    [DataMember]
    public float AverageTemperature…

    [DataMember]
    public bool TemperatureIsInCelcius…
  }

Der Aufwand für die Implementierung des Vertrags in unserem Beispiel mit dem Fahrradcomputer ist minimal (siehe Abbildung 6).

Abbildung 6 Implementierung des Vertrags

public class BikeComputerService : IBikeComputerService
{
  static BikeComputerData _lastData = null;

  public BikeComputerData GetLastComputerData()
  {
    if (_lastData != null)
    {
      return _lastData;
    }
    return new BikeComputerData();
  }

  public void UploadBikeComputerData(BikeComputerData rideData)
  {
    _lastData = rideData;
  }
}

Definieren des Diensts durch die WSDL-Datei

Auf Basis des erstellten Vertrags und Schemas generiert der Windows Azure-Dienst automatisch eine WSDL-Datei (Web Service Definition Language). Sie enthält die SML-Definition (Service Modeling Language) des Diensts. Die W3C-Spezifikation für das WSDL-Dokument finden Sie unter w3.org/TR/wsdl. Sie definiert, welche Operationen und Meldungen vom Dienst unterstützt werden. Die WSDL-Datei wird als eine XML-Beschreibung auf einer Website gespeichert, auf die jeder zugreifen kann, der eine Verbindung zum Dienst herstellt. Unsere finden Sie unter netmfbikecomputerservice.cloudapp.net/BikeComputerService.svc?wsdl. Abbildung 7 zeigt einen kleinen Snapshot der WSDL-Datei, damit Sie einen Eindruck bekommen, aber denken Sie daran, dass diese Datei automatisch erstellt wird und nur von anderen Programmen verwendet wird. Sie müssen einen solch komplexen und schwierigen XML-Code nicht selber schreiben.


image: The WSDL File
(Zum Vergrößern auf das Bild klicken)

Abbildung 7 Die WSDL-Datei

Sie sehen, dass die WSDL-Datei die Definitionen für Meldungen zur Dateneingabe und -ausgabe sowie Operationen zum Abrufen und Hochladen von Daten enthält. Nun haben wir unsere einfache Oberfläche für den Dienst definiert und veröffentlicht, aber wie programmiere ich das? Auch das ist ganz einfach.

Generieren von Code mit „MFSvcUtil.exe“

Auf dem Desktop befindet sich ein Dienstprogramm namens „ServiceModel MetadataUtility Tool“ (SvcUtil.exe), mit dem Service Model-Code aus Metadatendokumenten (wie unsere WSDL-Datei) und umgekehrt erstellt werden kann. Das .NET Micro Framework verfügt über ein ähnliches Programm namens „MFSvcUtil.exe“. Dabei handelt es sich um ein Befehlszeilentool, das am besten im Projektverzeichnis ausgeführt wird. Wir führen es also aus, indem wir einen Verweis auf die veröffentlichte WSDL-Spezifikation erstellen.

<SDK_TOOLS_PATH>\MFSvcUtil.exe http://netmfbikecomputerservice.cloudapp.net/BikeComputerService.svc?wsdl

Mit dem Tool werden drei Dateien generiert (siehe Abbildung 8).

image: Executing the MFSvcUtil.exe Command
(Zum Vergrößern auf das Bild klicken)

Abbildung 8 Ausführen des Befehls „MFSvcUtil.exe“

Die Datei „BikeComputerService.cs“ enthält die Definition der Daten in den Meldungen, die Klassen, mit denen die vom Dienst unterstützten Operationen definiert werden (da unser Gerät ein Client ist) sowie eine Reihe von Hilfsfunktionen zum Serialisieren und Deserialisieren der Daten (siehe Abbildung 9).

Abbildung 9 Die Datei „BikeComputerService.cs“

namespace BikeComputer.org
{
  [DataContract(Namespace="http://tempuri.org/")]
  public class GetLastComputerData ...
    
  public class GetLastComputerDataDataContractSerializer : DataContractSerializer…
    
  [DataContract(Namespace="http://tempuri.org/")]
  public class GetLastComputerDataResponse ...
    
  public class GetLastComputerDataResponseDataContractSerializer : DataContractSerializer…
    
  [DataContract(Namespace="http://tempuri.org/")]
  public class UploadBikeComputerData ...
    
  public class UploadBikeComputerDataDataContractSerializer : DataContractSerializer…
    
  [ServiceContract(Namespace="http://tempuri.org/")]
  [PolicyAssertion(Namespace="https://schemas.xmlsoap.org/ws/2004/09/policy",
    Name="ExactlyOne",
    PolicyID="WSHttpBinding_IBikeComputerService_policy")]
  public interface IIBikeComputerService ...
}
namespace schemas.datacontract.org.BikeComputerServiceWebRole...

Die Datei „BikeComputerClientProxy.cs“ enthält die Proxyschnittstellen für den Webdienst:

namespace BikeComputer.org
{
  public class IBikeComputerServiceClientProxy : DpwsClient
  {
    private IRequestChannel m_requestChannel = null;
        
    public IBikeComputerServiceClientProxy(Binding binding,    
      ProtocolVersion version) : base(binding, version)...
        
    public virtual GetLastComputerDataResponse 
      GetLastComputerData(GetLastComputerData req) ...
        
    public virtual UploadBikeComputerDataResponse  
      UploadBikeComputerData(UploadBikeComputerData req) ...        
  }
}

Die dritte von „MFSvcUtil.exe“ erstellte Datei ist „BikeComputerServiceHostedService.cs“. Diese enthält die Oberflächenlogik, die auf der Dienstseite ausgeführt wird. In unserem Fall wurde die WSDL-Datei aus den erstellten Dienst- und Datenverträgen generiert, wir brauchen sie also nicht. Sie ist für Szenarios erforderlich, in denen Sie eine veröffentlichte WSDL erhalten und daraus einen Dienst replizieren möchten oder in denen Sie einen Dienst auf einem anderen Gerät ausführen möchten. Beachten Sie, dass Geräte Clients, Server oder beides sein können. Dass Geräte Dienste für andere Dienste bereitstellen können, ermöglicht einige interessante Anwendungen. „BikeComputerServiceHostedService“ enthält:

namespace BikeComputer.org
{
  public class IBikeComputerServiceClientProxy : DpwsHostedService
  {
    private IIBikeComputerService m_service;
        
    public IBikeComputerService(IIBikeComputerService service, 
      ProtocolVersion version) : base(version) ...

    public IBikeComputerService(IIBikeComputerService service) :
      this(service, new ProtocolVersion10())...

    public virtual WsMessage GetLastComputerData(WsMessage request) ...

    public virtual WSMessage UploadBikeComputerData(WsMessage request) ...
  }
}

Hochladen der Daten

Wie Sie gesehen haben, wurde der Code für die Geräteanwendung bisher automatisch aus der WSDL-Datei erstellt. Welchen Code müssen Sie nun aber für den Client schreiben, damit dieser die Verbindung zum Dienst herstellt, Ihre Daten veröffentlicht und sie auch wieder einliest, um sicherzustellen, dass sie empfangen wurden? Sie benötigen nur einige wenige Codezeilen. Für unseren Fahrradcomputer bin ich wie folgt vorgegangen.

In der RideDataModel-Klasse habe ich zum Einrichten der DPWS-Verbindung folgenden Code zum Konstruktor hinzugefügt:

public RideDataModel()
{
  _currentRideData = new CurrentRideData();
  _summaryRideData = new SummaryRideData();
  _today = DateTime.Now; //change this to the time service later.

  //--Setup the Web Service Connection
  WS2007HttpBinding binding = new WS2007HttpBinding(
    new HttpTransportBindingConfig(new Uri
      ("http://netmfbikecomputerservice.cloudapp.net/BikeComputerService.svc")
     ));

  m_proxy = new
    IBikeComputerServiceClientProxy(
    binding, new ProtocolVersion11());    

  _upload = new 
    UploadBikeComputerData();

  _upload.rideData = new
    schemas.datacontract.org. 
    BikeComputerServiceWebRole.
    BikeComputerData();
  }

Dann habe ich in dieser Klasse eine Methode erstellt, um die Daten zum Webdienst hochzuladen. Mit dieser Routine werden meine Übersichtsdaten aus der Tour in die Felder für das Webdienstschema übertragen (Verweis in der WSDL-Datei und Anzeige in „BikeComputerService.cs“), das diesen Daten entsprechen soll. Anschließend rufe ich die UploadBikeComputerData-Methode des Proxys mit den Daten zum Hochladen auf und rufe im Webdienst das Datum der letzten Tour ab, um zu überprüfen, ob meine Daten empfangen wurden (siehe Abbildung 10).

Abbildung 10 Hochladen der Daten in den Webdienst

public bool postDataToWS()
{
  //-- Load the ride summary data into the upload fields --//

  _upload.rideData.AverageCadence = _summaryRideData.averageCadence;
  _upload.rideData.AverageIncline = _summaryRideData.averageIncline;
  _upload.rideData.AverageSpeed = _summaryRideData.averageSpeed;
  _upload.rideData.AverageTemperature = 
    _summaryRideData.averageTemperature;
  _upload.rideData.Date = _summaryRideData.rideDate;
  _upload.rideData.Distance = _summaryRideData.distance;
  _upload.rideData.RidingTime = _summaryRideData.ridingTime;
  _upload.rideData.StartTime = _summaryRideData.startTime;

  //-- Upload the data --//

  m_proxy.UploadBikeComputerData(_upload);

  //-- Validate the upload by retrieving the data and comparing --//

  GetLastComputerData req = new GetLastComputerData();

  GetLastComputerDataResponse back = m_proxy.GetLastComputerData(req);

  if (back.GetLastComputerDataResult.Date == _upload.rideData.Date)
  {
    return false;
  }

  return true;
}

Hier wird angenommen, dass Sie nur eine Tour am Tag unternehmen, daher müssen Sie die Vergleichslogik ändern, wenn Sie öfter fahren. Ich gehe davon aus, dass auf dem Computer das Feature „Pause“ verwendet wird und behandele alle an einem Tag unternommenen Touren als einen Datensatz. Wie in einem früheren Blogbeitrag beschrieben, bin ich bereits in der Lage, die Daten in einer Datei oder auf einer SD-Karte im Fahrrad zu speichern. Ich richte noch die Option ein, zu verfolgen, welche Tourdaten im Webdienst veröffentlicht wurden und alle noch fehlenden Daten zu ergänzen. Dadurch kann ich den Webdienst auch nachträglich aktualisieren, wenn ich zwischendurch einmal für einige Zeit keine Verbindung hatte. Eine andere Erweiterung besteht darin, zwischendurch die Verbindung zu einem PC herzustellen, wenn mein Heimnetzwerk nicht verfügbar ist.

Also 17 Codezeilen, die ich in die Anwendung geschrieben habe (die meisten zum Übertragen der Übersichtsdaten in die Felder des Dienstschemas), und ich lade Daten zu einem Dienst hoch und nehme Gültigkeitsprüfungen vor. Nicht schlecht.

Am Ende der Tour verbunden

Wenn ich nach einer Tour in meine Garage komme, kann ich mit einer einfachen Bewegung den Bildschirm zum Speichern der Daten aufrufen und meine Daten in die Cloud hochladen.

Hier wäre noch einiges zu tun, um diese Daten besser nutzbar zu machen, aber das ist eine andere Geschichte.

Eingebettete Geräte waren bisher Inseln spezieller Technologie, bei denen mehr Wert auf einfache Programmierung und flexible Bedienung gelegt wurde als auf geringen Aufwand, niedrige Kosten und hohe Leistung. Mittlerweile kommen aber immer häufiger kleine Geräte zum Einsatz, die mit anderen Geräten und Netzwerken verbunden werden und so beeindruckende Lösungen schaffen. Gleichzeitig sinkt der Preis von Prozessoren und Speicher immer weiter, sodass wir nicht länger auf die Produktivität von Desktoptools und Sprachen verzichten müssen, um leistungsfähige und preisgünstige Geräte zu erstellen. Ergebnis ist, dass .NET-Programmierer nun sowohl von den Funktionen als auch von den Chancen profitieren, die die Arbeit auf kleinen Geräten mit sich bringen.

Durch das Webdienstmodell können diese kleinen Geräte miteinander verbunden werden, denn das Erkennen von Remotediensten, das Abonnieren von Remoteereignissen und der Austausch von dienstbezogenen Daten über Metadaten ermöglicht den flexiblen Anschluss zahlreicher Geräte. Der Nachteil ist, dass die Verbindung über SOAP und XML recht aufwändig ist und sich daher nicht in jedem Fall eignet.

Das Fahrradcomputerprojekt zeigt, dass Sie durch die Programmierfunktionen von .NET Framework in der Lage sind, überzeugende Benutzeroberflächen für kleine Geräte zu erstellen, Treiber für eine Vielzahl von Sensoren zu schreiben und diese Geräte mit der Cloud zu verbinden. Wie ich Ihnen vorgeführt habe, muss dazu der Webdienst definiert werden. Der auf dem Gerät ausgeführte Code wird zum größten Teil automatisch von „MFSvcUtil.exe“ generiert. Zum Hochladen der Daten waren nur einige zusätzliche Codezeilen erforderlich. Für andere Anwendungen ist möglicherweise das Suchen nach dem Webdienst erforderlich (WS_Discovery), das Abonnieren von Ereignissen an anderen Endpunkten (WS_Eventing) oder das Ausführen von Geräten und Diensten mit unterschiedlichen Funktionen (WS_MetaDataExchange). Alle können bei Bedarf zum grundlegenden Datenaustauschmodell hinzugefügt werden.

Wie ein Freund von mir einmal meinte, können .NET Framework-Programmierer nun den Zusatz „Embedded-Programmierer“ auf ihre Visitenkarten drucken lassen. Wir freuen uns, in den Diskussionen unter netmf.com zu erfahren, was für Geräte Sie erstellt haben. Dort beantworte ich auch Fragen, die Sie zu diesem Artikel oder dem Blog haben.

Viel Spaß beim Radfahren!

Colin Miller begann als wissenschaftlicher Programmierer mit der Erstellung von experimentellen 8- und 16-Bit-Steuerungssystemen. Dann wechselte er den Bereich und arbeitete 25 Jahre (davon 15 bei Microsoft) mit PC-Software, darunter Datenbanken, DTP, Konsumgüter, Word, Internet Explorer, Passport (LiveID) und Onlinedienste. Als Product Unit Manager für das .NET Micro Framework konnte er dann die unterschiedlichen Karrierewege wieder glücklich vereinen.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Jane Lawrence und Patrick Butler Monterde