Sonntag, 3. März 2013

Daten vom Arduino verarbeiten und visualisieren

image
Arduino Nano 3.0 und der Magnet Sensor HMC5883L.
Gelb=SDA (Pin4), Orange=SCL (Pin5), Rot=+5V und Schwarz=GND

Wenn ich keine Lösung für dem Netduino habe, dann finde ich eine für Arduino. Eines meiner ersten Programme war eines zum Lesen der Sensordaten. Zunächst verwendete ich den Arduino Monitor, der allerdings nichts anderes tat, als eine serielle Verbindung herzustellen. Mit ein “wenig” C# und .NET Framework Kenntnis kann auch ein eigenes Programm geschrieben werden, das die Daten ebenfalls lesen kann. Mein neues Bespiel zeigt die magnetischen Einflüsse auf 3 Achsen an, was ich mit Hilfe von Windows Forms visualisiere.

image
Windows Forms mit analoger Anzeige

Warum Windows Forms? Die GUI ist schon etwas in die Jahre gekommen und bietet kaum Neuerungen, dennoch kann man grafisch immer noch etwas an Ergebnissen erreichen. Also wäre es doch schön, eine analoge Anzeige auf digitalem Boden zu schaffen. Aber erstmal eins nach dem anderen.

Auf der Seite Gihub stellt der Entwickler Jeff Roberg seine Bibliotheken zur Verfügung für Zahlreiche Sensoren die mit dem Arduino angesprochen werden können. Für mein Beispiel wird der Ordner HMC5883L und die I2Cdev Inhalt benötigt, die dann in den Order “libraries” vom Arduino Compiler kopiert werden. Anschließend kann das mitgelieferte Beispiel “HMC5883L_Example” geöffnet und auf dem Arduino hochgeladen werden. Die Ausgabe erfolgt über die serielle Verbindung, die auf dem Arduino Monitor betrachtet werden kann.

image
Ausgabe über den Monitor

Nun zur PC Anwendung, die die Daten auswertet und sie in einer visuellen Form darstellt. Als erstes stellen wir die Verbindung sicher, und hier hilft uns das .NET Framework mit der Klasse “SerialPort”. Damit wir die Einstellungen auch später in der Anwendung anpassen können, benötigen wir eine kleine Setup Möglichkeit. Hier reichen die Auswahl des Ports und der Baudrate.

image
Das kleine Setup mit einem Button zum “Aktualisieren” und einem für “Verbinden”.

In “Form1.cs” Code Seite kommt zunächst die Event Methode, die durch einen Doppelklick auf dem Button im Designer erstellt wird. Dort schreiben wir etwas Code, um die Auswahl über die ComboBoxen auszufüllen.

private void buttonUpdate_Click(object sender, EventArgs e)
{
    comboBoxChooseCom.Items.Clear();
    foreach (string item in SerialPort.GetPortNames())
    {
       comboBoxChooseCom.Items.Add(item);
    }

    comboBoxChooseBaud.Items.Clear();
    comboBoxChooseBaud.Items.Add(9600);
    comboBoxChooseBaud.Items.Add(38400);
    comboBoxChooseBaud.Items.Add(115200);
}

Füllt die ComboBox mit den Einstellungen.

Die SerielPort Klasse wird nun als Member in der “Form1.cs” angelegt. Einen weiteren Button, der den Text “Verbinden” erhält, erstellen wir ebenfalls durch Doppelklick auf die Event Methode. Hier initialisieren wir die Klasse mit den Einstellungen aus der ComboBox. Damit die Anwendung fehlerfrei läuft, sollte vorher geprüft werden, ob eine Auswahl besteht. Nach erfolgreicher Verbindung muss noch ein neuer Event erzeugt werden, der immer ausgeführt wird, sobald am seriellen Port neue Daten verfügbar sind.

private void buttonConnect_Click(object sender, EventArgs e)
{
     if (!string.IsNullOrEmpty(comboBoxChooseCom.Text) &
          !string.IsNullOrEmpty(comboBoxChooseBaud.Text) &

          _SerialPort == null)
     {
         _SerialPort = new SerialPort(
                 comboBoxChooseCom.Text,
                 (int)comboBoxChooseBaud.SelectedItem,
                 Parity.None, 8, StopBits.One);
         _SerialPort.DataReceived +=
                 new SerialDataReceivedEventHandler(
                 _SerialPort_DataReceived);

         _SerialPort.Open();
     }
}

private void _SerialPort_DataReceived(
     object sender, SerialDataReceivedEventArgs e)
{

}

Mit den Einstellungen die Verbindung herstellen.

Jetzt kommen wir zum visuellen Teil der Anwendung, wofür wir ein neues UserControl anlegen, indem wir in unserem Projekt mit Rechtsklick das Kontextmenü öffnen und dann über Hinzufügen >  Neues Element.. das Benutzersteuerelement auswählen. Zuvor sollte ein entsprechender Name verwendet werden wie “AnalogView.cs” oder ähnliches. In meinem Bespiel hat das UserControl eine Größe von 200x100 Punkten, was über die Layout Eigenschaften festgelegt werden kann. Anschließend geht es auf der Code Seite weiter.

public partial class UserControlAnalogView : UserControl
{
   private Point _PointerValue = new Point(100, 20);
   private double _DisplayRawValue = 0.0;
    private double _MaximumValue = 90;

   public double Maxi_MinimumValue
    {
        set
        {
            _MaximumValue = value;
            _MinimumValue = 0 - _MaximumValue;
        }
       get { return _MaximumValue; }
    }

    public UserControlAnalogView()
    {
        InitializeComponent();
    }

    public void RenderAnalogDisplay()
    {
        SuspendLayout();

       Graphics g = CreateGraphics();
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

        Pen p = new Pen(new SolidBrush(Color.Black), 2);
        p.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;

        g.FillPie(Brushes.WhiteSmoke, 5, 5, 190, 190, 0, -180);

        g.DrawPie(p, 5, 5, 190, 190, 0, -180);

        p.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
        p.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
        g.DrawLine(p, new Point(100, 100), _PointerValue);

        ResumeLayout(false);
    }

    public double DisplayValue
    {
        set
        {
            _DisplayRawValue = value;

            double alpha = _DisplayRawValue / _MaximumValue;

            double w1 = Math.Cos(alpha) * 90;
            double w2 = Math.Sin(alpha) * 90;

            _PointerValue = new Point((100 + Convert.ToInt32(w2)), 100 - Convert.ToInt32(w1));

            RenderAnalogDisplay();
        }
        get { return _DisplayRawValue; }
    }
}

Der Codeabschnitt für das Erstellen der Analoganzeige. Die Methode “RenderAnalogDisplay()” zeichnet die Anzeige, und über “DisplayValue” wird die Zeigerposition neu ausgerichtet.

Das Benutzersteuerelement ist fertig und nach dem Drücken der F6 Taste steht es auch schon in unser Toolbox, wo wir es in der Designeransicht auf die Form1 ziehen können. Für jede Achse wird eine analoge Anzeige verwendet; man stelle in den Eigenschaften das Maximum auf 4000 ein.

image
Die Analoge Anzeige wird erst gerendert, wenn die Methode im UserControl “RenderAnalogDisplay()” ausgeführt wird.

Als nächstes kommt das Auslesen der empfangen Werte. Der Inhalt sind Konstanten und können durch eine Methode einfach ausgewertet werden.

Raw: 455  39  668  Scaled:  418.60  35.88  614.56  Heading:  0.13 Radians  7.52 Degrees 
Die Zeichenkette, die uns der Arduino nach dem lesen des Sensor übermittelt.

Im Beispiel beschränken wir uns auf die Raw Daten und programmieren entsprechend in die Event Methode vom SerialPort den Lesevorgang und schreiben das Ergebnis an die analogen Anzeigen.

private void _SerialPort_DataReceived(
      object sender, SerialDataReceivedEventArgs e)
{
        string content = _SerialPort.ReadLine();

       string[] sa = content.Split('\t');
       string[] sa2 = sa[1].Split(' ');

        int[] ia = new int[3];
        int axis = 0;
        for (int i = 0; i < sa2.Length; i++)
        {
            if (!string.IsNullOrEmpty(sa2[i]))
            {
               int.TryParse(sa2[i], out ia[axis]);
                axis++;
            }
        }

        userControlAnalogView1.DisplayValue = ia[0];
        userControlAnalogView2.DisplayValue = ia[1];
        userControlAnalogView3.DisplayValue = ia[2];
    }
}

Methode für das Einlesen der Raw Daten.

An dieser Stelle sind wir mit dem Beispiel fertig und die Anwendung kann nun über F5 gestartet werden. Mit Aktualisieren erhalten wir die verfügbaren COM Verbindungen und wählen natürlich die vom Arduino.

Nähert man sich auf einige Zentimeter an den Sensor bewegen sich die Zeiger.

Mit einem Magneten können wir das Magnetfeld um den Sensor manipulieren und sehen, wie die Zeiger ausschlagen. Mit ein paar weiteren Codezeilen lässt sich auch der Rest der Daten anzeigen, allerdings verbleibe ich dabei, mich möglichst kurz zu halten.

Zuletzt darf die bereits fertiggestellte und kommentierte Solution des kleinen Tools nicht fehlen:

Arduino Nano 3.0 und MPU6050 in Quellcode Kurzfassung

image
Verkabelungsbeschreibung ist am ende dieses Blogs.

Bereits Anfang des Jahres habe ich eine Lösung in C#  und .NET Micro Framework geschrieben. Ursprünglich habe ich die Lösung aus der in C++ geschriebenen Library portiert (Library für Arduino). Zuvor suchte ich nach einem kürzeren und funktionalem Beispiel, aber ohne Erfolg. Da ich nun selbst relativ gut mit dem Sensor vertraut bin, konnte ich ein kürzeres Beispiel für den Arduino schreiben.

#include <Wire.h>

#define sensor_address 0x68

void setup()
{
  // 9600 Baut reichen zum Betrachten aus.

  Serial.begin(9600);
  Wire.begin();

  delay(1000);
 
  // Powermanagement aufrufen
  // Sensor schlafen und Reset, Clock wird zunächst von Gyro-Achse Z verwendet
 
  Serial.println("Powermanagement aufrufen - Reset");
  SetConfiguration(0x6B, 0x80);
 
  // Kurz warten
  delay(500);
 
  // Powermanagement aufrufen
  // Sleep beenden und Clock von Gyroskopeachse X verwenden

  Serial.println("Powermanagement aufrufen - Clock festlegen");
  SetConfiguration(0x6B, 0x03);
 
  delay(500);
 
  // Konfigruation  aufrufen
  // Default => Acc=260Hz, Delay=0ms, Gyro=256Hz, Delay=0.98ms, Fs=8kHz

  Serial.println("Konfiguration aufrufen - Default Acc = 260Hz, Delay = 0ms");
  SetConfiguration(0x1A, 0x00);
 
  // Leerzeichen
  Serial.println("");
}

void loop()
{
  byte result[14];
  // Anfangs Adresse von Beschleunigungssensorachse X
  result[0] = 0x3B;
 
   // Aufruf des MPU6050 Sensor
  Wire.beginTransmission(sensor_address);
  // Anfangsadresse verwenden.
  Wire.write(result[0]);
  Wire.endTransmission();
  // 14 Bytes kommen als Antwort
  Wire.requestFrom(sensor_address, 14);
  // Bytes im Array ablegen
  for(int i = 0; i < 14; i++)
  {
    result[i] = Wire.read();
  }
 
  // Zwei Bytes ergeben eine Achsen Wert und könenn

// per Bit Shifting  zusammengelegt werden.
 
  // Beschleunigungssensor
  int acc_X = (((int)result[0]) << 8) | result[1];
  int acc_Y = (((int)result[2]) << 8) | result[3];
  int acc_Z = (((int)result[4]) << 8) | result[5];
 
  // Temperatur sensor
  int temp = (((int)result[6]) << 8) | result[7];
 
  // Gyroskopesensor
  int gyr_X = (((int)result[8]) << 8) | result[9];
  int gyr_Y = (((int)result[10]) << 8) | result[11];
  int gyr_Z = (((int)result[12]) << 8) | result[13];
 
  // Ausgabe
  Serial.print("ACC X:\t");
  Serial.print(acc_X); Serial.print("\t");
  Serial.print("Y:\t");
  Serial.print(acc_Y); Serial.print("\t");
  Serial.print("Z:\t");
  Serial.print(acc_Z); Serial.print("\t");
  Serial.print("Temperatur:\t");
  Serial.print(temp); Serial.print("\t");
  Serial.print("Gyroskope X:\t");
  Serial.print(gyr_X); Serial.print("\t");
  Serial.print("Y:\t");
  Serial.print(gyr_Y); Serial.print("\t");
  Serial.print("Z:\t");
  Serial.print(gyr_Z); Serial.println("\t");
}

void SetConfiguration(byte reg, byte setting)
{
   // Aufruf des MPU6050 Sensor
  Wire.beginTransmission(sensor_address);
  // Register Aufruf
  Wire.write(reg);
  // Einstellungsbyte für das Register senden
  Wire.write(setting);
  Wire.endTransmission();
}

Kurzfassung für den Sensor Aufruf, ohne weitere Library.

Mit dem Quellcode wird der Sensor mit den Standardwerten initialisiert, so dass die Empfindlichkeit des Gyroskop bei 250°/s und der Beschleunigungssensor bei 2g liegt. Steht der Sensor still auf dem Tisch sollten die Werte für das Gyroskop und den Beschleunigungssensor für X und Y Achse nahe bei 65536 oder 0 liegen. Beschleunigung auf der Z-Achse bei ca. 16000.

image
Ausgabe über dem Arduino Monitor

Ich hoffe, ich konnte somit dem einen oder anderen mit diesem kleinen Bespiel weiterhelfen. Bei Fragen kann gerne die Kommentarfunktion verwendet werden.

Anschluss Beschreibung:

  • Arduino Nano 3.0 – Pin4 –> MPU6050 Pin SDA
  • Arduino Nano 3.0 – Pin5 –> MPU6050 Pin SCL
  • Arduino Nano 3.0 – Pin5V –> MPU6050 Pin VCC
  • Arduino Nano 3.0 – PinGND –> MPU6050 Pin GND

 

Zuletzt geändert 20.11.2014

Montag, 11. Februar 2013

LED 8x8 Matrix (Rot/Grün) Schaltung

Led8x8Matrix_01
Die LED 8x8 Matrix an meiner Schaltung.

So einige wie ich haben seltsamerweise kein Dokument zu der LED Matrix im Internet gefunden. Letzten Endes habe ich dann selbst durch einiges Prüfen herausgefunden, welcher Pin nun für welche Funktion ist. Anschließend konnte ich dann einen Versuchsaufbau starten, der allerdings aufwendiger wurde. Auf den ersten Blick sieht meine Schaltung mit den vielen Kabeln ziemlich unüberschaubar aus. Dennoch habe ich den Aufbau mal wieder aus dem Kopf heraus zusammengesteckt, woran man sieht, dass diese Schaltung nicht sonderlich kompliziert sein kann.

image
Die Verwendeten ICs. Shiftregister, 2x Demultiplexer und 2x Inverter

Für die Schaltung habe ich Folgendes verwendet:

  • LED 8x8 Matrix (Rot/Grün)
  • Shiftregister (74hc595)
  • 2x Demultiplexer (74HC154)
  • 2x Inverter (74F04)
  • 8x Widerstände (150 Ohm)
  • 2x kurze Breadboards
  • 1x langer Breadboard
  • und viel Kabel zum Verbinden

Der Netduino wird hier als Steuereinheit verwendet, auf dem später meine Library “Game of Life” läuft.

Der Shiftregister ist entscheidend für die Darstellung an der LED-Matrix, da diese Einheit den Zustand der Demultiplexer schaltet, wenn alle 8 Bits gesetzt wurden. Im meinem ersten Versuch habe ich die zwei Demultiplexer direkt am Netduino angeschlossen. Hierbei wurden mehrere LEDs als gesetzt geschaltet. Der Fehler ließ sich jedoch nachvollziehen. In einer Methode habe ich die 8 Ausgänge geschaltet, die sehr zeitnah nacheinander ausgeführt werden. Leider war dieser Vorgang nicht schnell genug, so dass sich während dieser Zeit auch andere Schaltzustände ergaben. Zwar leuchten die nicht gesetzten LEDs nicht hell, aber dennoch stört dieser Zustand.

Schaltbild_LED_Matrix_8x8
Schaltbild von der LED Matrix.

An der LED Matrix sind insgesamt 24 Pins und deswegen sind es zum Anschließen an einen einzelnen Netduino oder Arduino zu viel. Abhilfe schafft hier der Demultiplexer, der mit seinen 4 Eingängen bis zu 16 Zustände annehmen kann. Da einer nicht reicht, kommt ein zweiter hinzu, und wenn man diese in eine Matrix einbindet, dann können bis zu 256 Zustände erreicht werden. Den ersten Demultiplexer verwende ich für die 8 Reihen Rot und 8 Reihen Grün. Der zweite ist für die 8 Zeilen, die vorher durch einen Inverter geschaltet werden. Grund hierfür sind die Signale an den Demultiplexerausgängen, die negiert sind. Die Widerstände teilen die Spannung auf und trimmen die Stromaufnahme bei 20mA. Jedoch hier der Hinweis, dass ich die Spannung der LEDs auf 2.0V schätze. Also ergibt sich aus meiner Rechnung, dass am Widerstand 3V anliegen sollten. Mit der Grundformel R=U/I erhält man den benötigten Widerstandswert von 150Ohm. Im folgenden habe ich mir aus meinem Beispiel ein Schaltbild in Eagle erstellt. Was hier allerdings nicht so schön ist,ist dass die Last von den LEDs direkt an den ICs liegt. Also ist diese Schaltung aus diesem Grunde nur experimentell und nicht für den Dauerbetrieb geeignet. 

Schaltbild_LED_Matrix_8x8_Gesamt
Gesamtes Schaltbild, ohne Netduino.

Nun ein bisschen Quellcode zum Ende, den ich jedoch von Netduino.com abgeschrieben und etwas abgeändert habe. Ich empfehle auf der Seite nachzuschauen, falls die Verbindungen mit Shiftregister und Netduino unklar sein sollten.

private static OutputPort _Data = new OutputPort(Pins.GPIO_PIN_D0, false);
private static OutputPort _Clock = new OutputPort(Pins.GPIO_PIN_D1, false);
private static OutputPort _Latch = new OutputPort(Pins.GPIO_PIN_D2, false);

/// Code von der Seite Netduino
/// Der Besipeil code und ein Schaltbild

private static void UpdateLEDs(int value)
{
    _Latch.Write(false);

    for (int i = 0; i < 8; i++)
    {
        int bit = value & 0x80;
        value = value << 1;

        if (bit == 128)
        {
            _Data.Write(true);
        }
        else
        {
            _Data.Write(false);
        }

        _Clock.Write(true);
        //Thread.Sleep(1);
        SleepWhile(5);
        _Clock.Write(false);
    }
    _Latch.Write(true);
}

/// Dient als Ersatz zu Thread.Sleep
private static void SleepWhile(int count)
{
    for (int i = 0; i < count; i++){ }
}

Members und die Methode für das ansteuern des Shiftregister.

Wird nun an der Methode ein Wert zwischen 0 und 255 übergeben, wird die entsprechende Bit Einstellung am Shiftregister gesetzt. Dieser steuert die zwei Demultiplexer, die wiederum mit der LED Matrix verbunden sind und die entsprechende LED leuchtet.

Normalerweise würde man hier einen LED Matrix Treiber verwenden, um sich die umfangreiche Verkabelung zu sparen. Jedoch möchte ich zeigen, dass es auch andere Lösungen gibt und hoffe, dass dieses Bespiel dem einen oder anderen weiterhilft.

Mein Beispiel mit “Game of Life” und einem Gleiter.

Sonntag, 3. Februar 2013

UART WiFi Server Client Module kann auch senden

Netduino_WiFi_01
Netduino Plus, Shield, WiFi Module und der Sensor

Diesmal möchte ich ein Beispiel beschreiben, in dem nicht der Netduino der Empfänger ist, sondern der Sender, ohne dass auf die Serverfunktion verzichtet werden muss. Der Quellcode vom letzten Blog-Post wird an Server und Client seitig erweitert. Am Server (Netduino) Quellcode ist dies sogar recht simpel, da man an dieser Stelle nur vier weitere Zeilen Code hinzufügen muss:

while (true)
{
      byte[] message = Encoding.UTF8.GetBytes("DateTime: " + DateTime.Now.ToString());
      _SerialPort.Write(message, 0, message.Length);
      Thread.Sleep(1000);
}

Relativ simpel fällt der Code für das Senden aus.

In der “While”-Schleife ist nun zu sehen, dass ich die aktuelle Zeit als String wiedergebe und anschließend in ein Byte Array umwandle, um es für den Schreibvorgang vorzubereiten. Die “Write”-Methode selbst erledigt den Rest und gibt den Inhalt an das WiFi Modul weiter.

Am Client jedoch ist ein wenig mehr Arbeit notwendig. Wenn die Daten ankommen, muss auch ein Event erfolgen, um die Daten zu verarbeiten und schließlich an die Anwendung auszugeben.

image
Das Programm mit einem zusätzlichen TextBlock für den Empfang.

Für den Empfang der PC Anwendung habe ich eine recht einfache Lösung verwendet, die auch leicht nachvollziehbar ist. Leider ist diese aber nicht ideal, da es eventuell zur Vollauslastung kommt, wenn hier die Schleife nicht etwas gebremst wird. Ein besseres Beispiel gibt es auf MSDN, wie hier mit Hilfe der Socket Klasse ein Event erstellt werden kann.

Nun zur Erweiterung in der Client Socket Klasse, wo eine weitere Methode später im Thread ausgeführt wird. Die Thread-Klasse selbst kommt in den Member Bereich mit dem Namen “_ThreadWaitReceive”. Der Grund hierfür ist, dass später beim Schließen der Klasse der Thread beendet werden muss. Sonst läuft der Thread weiter, obwohl die Anwendung selbst lange beendet wurde.

private Thread _ThreadWaitReceive;

private void WaitReceive(object obj)
{
     byte[] buffer = new byte[1024];

     while (_Socket != null && buffer.Length > _Socket.Receive(buffer, SocketFlags.None))
     {
          string message = Encoding.UTF8.GetString(buffer).Replace('\0', ' ').Trim();
          ReceiveEvent(message);
          buffer = new byte[1024];
     }
}

Die zusätzliche Methode für den Empfang.

Mit der Encoding wird der String eingelesen und über die Methoden Replace() und Trim()  werden die leeren Inhalte gefiltert. Mit der folgenden Zeile wird dann der Inhalt an die Event Methode übergeben.

public delegate void NetworkReceiveEventHandler(object sender, NetworkStatusEventArgs e);
public event NetworkReceiveEventHandler NetworkReceiveEvent;
public virtual void ReceiveEvent(string message)
{
    if (NetworkReceiveEvent != null & !string.IsNullOrEmpty(message))
    {
         NetworkReceiveEvent(this, new NetworkStatusEventArgs(message.Trim()));
    }
}

Die Methode, mit Handler und Event.

Mit dem Event geht es weiter an die Oberfläche, um den Inhalt des Empfang in einem TextBlock lesen zu können.

public MainWindow()
{
    InitializeComponent();

    _Client.NetworkReceiveEvent += new ClientController.NetworkReceiveEventHandler(_Client_NetworkReceiveEvent);
}

private void _Client_NetworkReceiveEvent(object sender, NetworkStatusEventArgs e)
{
    Dispatcher.BeginInvoke(new WriteReceiveHandler(WriteReceive), new object[] { e.Message });
}

private delegate void WriteReceiveHandler(string message);

private void WriteReceive(string message)
{
    textBlockDataReceive.Text = message;
}

Event zugewiesen, Handler für die Oberfläche und Methode, um in die Textbox zu schreiben.

Mit ein paar Code Zeilen Ergänzungen ist nun die PC Anwendung und der Netduino imstande, in beide Richtungen zu kommunizieren. Sicherlich gibt es hier etwas besseres, als sich nur das DateTime anzuzeigen. Zwinkerndes Smiley

Wie bereits zuvor gibt es wieder das Ganze in zwei fertigen Solutions, mit den Quelltexten und natürlich auch den Kommentaren.

Die Solutions für den Download:

Sonntag, 27. Januar 2013

UART WiFi Server Client Module am Netduino

image
Netduino Plus, Shield, WiFi Module und der Sensor MPU6050

Bereits letztes Jahr habe ich einen Post darüber geschrieben, wie dieses Modul eingerichtet wird und mit einem Arduino verwendet werden kann. Schon lange ist es daher überfällig, dass ich auch ein Bespiel mit dem Netduino schreibe. Aber bevor ich darüber schreiben konnte, musste ich so einige Versuche anstellen.

Zuvor war das Ergebnis, dass über dem Webbrowser ein “Hello World!” auf dem Browser erschien. Diesmal kommt ein Beispiel auf dem Netduino,  das den Text im Debugger anzeigt. Leider ist es beim dem WiFi Modul nicht möglich mit der Socket Klasse zu arbeiten, so dass uns nur die serielle Verbindung bleibt. Und zugegeben, wie ich später herausfand, funktioniert dies besser und einfacher, als ich erwartet hatte.

image
Mein neuer Netduino Shield für WiFi Modul und Sensor

Zunächst die Verkabelung, wofür ich meinen Netduino Plus und einen neuen (eigens erstellten) Shield verwende, mit dem ich nun den Sensor und das WiFi Modul aufstecken kann. Es ist noch nicht fertig, reicht aber für dieses Beispiel aus. Kommen wir zu ein wenig Quellcode.

using System.Threading;
using SecretLabs.NETMF.Hardware.NetduinoPlus;
using System.Text;
using System.IO.Ports;
using Microsoft.SPOT;

namespace SerialPortExample
{
    public class Program
    {
        private static SerialPort _SerialPort;

        public static void Main()
        {
            Thread.Sleep(6000);

            _SerialPort = new SerialPort(SerialPorts.COM2, 115200);
            _SerialPort.DataReceived +=
new SerialDataReceivedEventHandler(_SerialPort_DataReceived);
            _SerialPort.Open();

           while (true)
           {
               Thread.Sleep(1000);
           }

        }

        private static void _SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;

            byte[] buffer = new byte[sp.BytesToRead];

            if (sp.Read(buffer, 0, buffer.Length) > 1)
            {
                char[] ca = Encoding.UTF8.GetChars(buffer);

                string reading = string.Empty;
               foreach (char item in ca)
                {
                    reading += item.ToString();
                }

                Debug.Print(reading);
            }
        }
    }
}

Inhalt aus der Program.cs klasse. Das “public Class” herum und using

Dieser Code Ausschnitt reicht bereits aus, um Daten vom WiFi Modul zu lesen, die empfangen wurden. Zwei Sachen sind hier nur wichtig: An welchen COM Port habe ich mein Modul angeschlossen? Und mit welcher Baud Rate wird kommuniziert?

Kommen wir zum nächsten Punkt. Damit die Daten beim WiFi Modul ankommen, brauche ich einen Client zum Senden. Grundsätzlich reicht ein Browser aus, um Daten per Http zu versenden. Einfach in der Adresszeile die IP Adresse und Port Nummer des WiFi Moduls eingeben und dahinter den gewünschten Inhalt. Falls dies nicht klappen sollte, dann noch ein “Http://” davor schreiben, da es vorkommen kann, dass die Integrierte Suchmaschine startet.

image
Eingabe in die Adresszeile des Browsers.

Sieht man sich die Ausgabe im Debugger an, so sieht das doch nach relativ viel aus. Neben dem Text “Test”, sind noch viele anderen Informationen übermittelt worden. Ob die Daten gebraucht werden, ist vom Projekt abhängig und ich denke, für den Anfang möchte man einfach nur den Text haben.

image
“Debug.Print” Ausgabe

Daher ist es sinnvoll hier gleich einen eigenen Windows Client zu schreiben, der einfach nur den Text übermittelt und das geht mit Hilfe der der Socket Klasse. Mein neues Beispiel ist ähnlich wie aus meinem Blog Post zu “(Teil 1) Netzwerkverbindung zwischen Windows Phone 7 und Netduino Plus”, nur dass dieses mal eine Anwendung auf dem PC geschrieben wird.

using System;
using System.Net.Sockets;
using System.Threading;
using System.Net;

namespace NetworkClient
{
    public class ClientController
    {
        private Socket _Socket = null;

        private int _Timeout = 5000;
        private Thread _Thread;
        private ManualResetEvent _ClientDone = new ManualResetEvent(false);
        private string _OperationResult = string.Empty;

        public string Connect(string hostAddress, int port)
        {
            _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            _Socket.ConnectAsync(
                CreateOperationEvent(
                new DnsEndPoint(hostAddress, port)));

            _ClientDone.WaitOne(_Timeout);

           return _OperationResult;
        }

        public void Send(byte[] data)
        {
           if (_Thread == null || !_Thread.IsAlive)
            {
                _Thread = new Thread(new ParameterizedThreadStart(Send_InThread));
                _Thread.Start(data);
            }
        }

        private void Send_InThread(object obj)
        {
            SocketAsyncEventArgs socketEvent = CreateOperationEvent(_Socket.RemoteEndPoint);
            socketEvent.SetBuffer((byte[])obj, 0, ((byte[])obj).Length);
            _Socket.SendToAsync(socketEvent);
            _ClientDone.WaitOne(_Timeout);
            StatusEvent(_OperationResult);
        }

        private SocketAsyncEventArgs CreateOperationEvent(EndPoint remoteEndPoint)
        {
            _OperationResult = "Operation Timeout";
           SocketAsyncEventArgs socketEvent = new SocketAsyncEventArgs();
            socketEvent.RemoteEndPoint = remoteEndPoint;

            socketEvent.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
            {
                _OperationResult = e.SocketError.ToString();
                _ClientDone.Set();
            });

            _ClientDone.Reset();

            return socketEvent;
        }

        public void Close()
        {
            if (_Socket != null)
            {
                if (_Socket.Connected)
                {
                    _Socket.Disconnect(true);
                    _Socket.Close();
                }
            }
        }

        public bool IsSending
        {
            get
            {
                if (_Thread != null && _Thread.ThreadState == ThreadState.Running)
                {
                    return true;
                }

                return false;
            }
        }

        public bool IsConnected
        {
            get {
           try{ return _Socket.Connected; }
            catch{ return false; }
                  }
        }

        public delegate void NetwokrStatusHandler(object sender, NetworkStatusEventArgs e);

        public event NetwokrStatusHandler NetworkStatus;

        public virtual void StatusEvent(string message)
        {
            if (NetworkStatus != null)
            {
                NetworkStatus(this, new NetworkStatusEventArgs(message));
            }
        }
    }
}

Klasse für eine Client Verbindung (Achtung, Eventklasse ist hier nicht gepostet, siehe Download)

Die Client Klasse unterscheidet sich kaum von der Client Klasse, die ich für das Windows Phone 7 bereits verwendet habe. Daher waren nur wenige Änderungen notwendig, um den Programmcode für den PC tauglich zu machen. Natürlich werde ich wieder hier den Quellcode mit Kommentaren als Download zur Verfügung stellen.

image
Beispiel Anwendung das über Socket Klasse den Text übermittelt.
image
Ergebnis des Empfangenen Text.

Leider ist das Beispiel nur für das Debuggen geeignet, es sei denn, am Netduino wird ein Display angeschlossen, das den Text ebenfalls wiedergeben kann. Aber mit etwas Kreativität findet sich sicherlich ein passendes Projekt zum umsetzen.

Ein kleiner Hinweis. Je nach dem, wie der Puffer eingestellt ist, kann ein Text abschneiden, der gesendet wurde. Das bedeutet, dass ggf. der Netduino am Serial Port, die Event Methode für den Empfang mehrmals aufgerufen wird.

Die Dateien für den Download:

Dienstag, 1. Januar 2013

MPU6050 Sensor auslesen mit dem Netduino

image
Netduino mit Shield - Aufsatz, für Sensor und Reglern
Für mein Quadrocopter Projekt habe ich vor einigen Monaten einen zusätzlichen Sensor benötigt, da ich mit einem Beschleunigungssensor allein Schwierigkeiten hätte, das Gleichgewicht und die Lage festzustellen. Um dies etwas kompakter zu gestalten, fand ich im Internet das MPU6050 Sensor Modul, das einen Beschleunigungssensor und Gyroskopsensor auf einem Chip hat. Über Ebay erhält man das Modul zu einem günstigen Preis, man muss allerdings mit einer gewissen Wartezeit rechnen, so ca.. 3-4 Wochen.
image
MPU6050 (Die Platine selbst, scheint für verschieden Sensor Typen gedacht zu sein, wegen der Aufschrift MPU-x050)
Im Arduino Umfeld scheint der Sensor seine Verbreitung gefunden zu haben. Das dürfte auch an der fertigen Library liegen, womit sich in nur wenigen Codezeilen der Sensor für seine eigenen Projekte einbinden lässt . Eine bereitgestellte Lösung für Netduino gab es dafür nicht, was mich um so mehr dazu bewegt hat, hierfür eine Lösung zu finden.
image
Der Sensor verbunden mit dem Arduino Nano 3.0
Mein Ansatz war, den verfügbaren Code in C++ nach C# und .Net Micro Framework zu portieren. Das hieß viele Stunden Quellcode und Dokumentation zum Sensor lesen. Die Dokumentation befasst sich überwiegend mit den Befehlen des Mikroprozessors vom Sensor, was aber anhand der Beschreibung nur schwer nachvollziehbar ist. Überwiegend hat mir der Quellcode selbst am weitesten geholfen, um die Prozessreihenfolge zu verstehen. Am Anfang funktionierte der Code nur teilweise und ich bekam einfach keine brauchbaren Daten. Mehrmals kontrollierte und überarbeite ich das Initialisieren des Sensors über die Monate hinweg. Dazu muss ich hinzufügen, dass ich nur wenig Zeit hatte.
image
Unregelmäßige Werte vom Sensor. (Ausgabe Wert wird hier verkürzt dargestellt zwischen 0 bis 100)
Bis gestern dachte ich, dass ich mit dem Initialisieren den falschen Byte Wert verwendet habe. Es stellte sich heraus, dass ich bereits beim Einbinden des Sensors über den I²C Bus richtig gelegen hatte. Eine fehlende Codezeile, die ich an einer anderen Stelle gesetzt habe, wirkte Wunder. Also wichtiger Hinweis: “Bevor die Werte gelesen werden können, muss der erste Bytewert zunächst an den Sensor geschrieben werden.” Anschließend bekam ich endlich konstante und brauchbare Daten.
image
Stabile Werte, die sich erst verändern, wenn ich den Sensor bewege.
Nun käme normalweise der Punkt, wo ich nun endlich den Quellcode posten würde. Stattdessen hänge ich einen link an, und biete dieses als Download an, da sich der Blogeintrag zu sehr in die länge ziehen würde.
Beispiel Solution für den Sensor MPU6050

Hinweis zum Beispiel Quellcode:
  • Der Quellcode dient als Beispiel und ist daher keine Lösung
  • Soweit ich konnte, habe ich überall Kommentare eingetragen
  • Ich bin für Fragen und (sinnvolle) Kritik offen

Nachtrag (07.07.2013):

Zu dem Thema habe ich eine neuen Blog Post veröffentlicht

Mittwoch, 5. Dezember 2012

Wie hoch ist die Spannung auf dem Netzwerkkabel?

image
Um sicherzugehen habe ich die Spannung auf dem Oszilloskop  gemessen. Pro Rasterfeld Y sind 0,5V und für X zwischen 1 bis 50 Nano Sekunden.

Seit einigen Wochen frage ich mich schon, welche Spannungen auf dem Netzwerkkabel zur Datenübertragung verwendet werden. Hierbei spreche ich vom gängigen Netzwerkanschluss über RJ45 für den Hausgebrauch.
Bevor ich herausfand, dass nur +/-1 Volt anliegen, dachte ich, es sei wesentlich mehr. Verunsichernd sind die gefährlichen Aussagen im Internet, wo man fast jede Spannungshöhe findet. Es kamen 5V, 15V, 48V und sogar 90V waren dabei. Zugegeben, ich läge wahrscheinlich schon im Grab und andere sicher auch, wenn ich mal zufällig die Kontakte berührt hätte, aus welchen Gründen auch immer. Jedoch kann ich berichten, dass ich schon das eine oder andere Mal einen elektrischen Schlag erhalten habe. Es liegt daher nahe, dass viele Menschen hier eine höhere Spannung vermuten.

image
Ein aufgeschnittenes Netzwerkkabel ermöglichte die Messung am Netzwerkkabel.

Hätte ich gleich nach den IEEE Standard gesehen, wäre mir für diese einfache Frage eine lange Suche erspart geblieben. Wobei ich das mehr überflogen habe als gelesen. Denn schließlich fasst die Dokumentation für den IEEE 802.3 Standard über 600 Seiten.

image
Die selben Einstellungen, allerdings mit einer Verbindung zwischen meinem Netduino und dem Access Point

Aber warum interessiert mich die Spannung auf dem Netzwerkkabel?
Es gibt einige Geräte, die laufen nicht mit +/-1 Volt, sondern mit +/-0,5 Volt. Dies stellte ich bereits im Sommer fest, als ich versuchte, eine Verbindung zwischen meinem Netduino und zu meinem Access Point über eine Kabelverbindung herzustellen. Es stellte sich heraus, dass beide auf der niedrigen Spannung arbeiten und somit keine wirkliche Kommunikation stattfinden konnte. Bei beiden Geräten wird ein normal betriebener Netzwerkanschluss vorausgesetzt.

image
Spannungsmessungen mit dem Oszilloskop.

Die Lösung ist, einen Switch dazwischen zu schalten. Und hier das “Aber”. Das sollte möglichst alles Mobil sein, also für eine Fernsteuereinheit, das ich für die Entwicklung meines Quadrocopter benötige. Momentan habe ich tatsächlich einen Switch, jedoch suche ich nach einer Verstärker Möglichkeit.

image
Derzeitig noch mit Montiert, würde jedoch gerne die paar Gramm einsparen.

Vielleicht muss ich mir auch einfach einen anderen Access Point suchen, der nicht auf diesen Sparfunktionen arbeitet und somit auch für das Projekt brauchbar wird.

Ameisen Simulation und andere Dinge

Wer lange sich schon mit C# beschäftig, hat sicherlich schon mal was von AntMe gelesen oder gehört. Diese Idee hatte ich aufgegriffen und mi...