Posts mit dem Label .NET Micro Framework werden angezeigt. Alle Posts anzeigen
Posts mit dem Label .NET Micro Framework werden angezeigt. Alle Posts anzeigen

Montag, 16. März 2020

Back to the Gadgeteer


Nachdem ich mit dem alten Lego Technic Control Center von 1990 den Plotter aufgebaut habe, will ich diesen auch steuern können. Leider hatte ich den Lego-Computer nicht mehr und für meinen heutigen Bedarf war er auch nicht mehr so interessant. Bei den vielen Microcontrollern, die ich heute besitze, stellte sich dann nur die Frage: Welcher sollte es werden?
Schließlich muss die Steuerung mit einem kleinen System funktionieren. Mit Lego Power Functions und Adapterkabel funktionierte das alles ganz gut, aber es stand kein Steuerungssystem dahinter, das ein komplettes Programm abspielen konnte.

Die Wahl hierfür fiel auf mein altes Gadgeteer FEZ Spider, welches mit C# und .NET Micro Framework programmiert werden kann, wie bei den Netduino. Hier fehlte mir zwar noch ein Motor Treiber, aber das kann ich noch nachträglich bauen.
Für mich war wichtig, dass ein Bildschirm zur Verfügung stände, mit dem man das abzuspielende Programm auswählen konnte.

Benötigt wird:
- Aktuelle Downloads
     netmf
     Microsoft's Gadgeteer Core (Doku und Beispiele)
     GHI Eletronics' NETMF SDK
- Alte Version, falls die Downloads nicht klappen. Zip enthält:
     netmfvs.vsix
     MicroFramorkSDK.MSI
     NETMF and Gadgeteer Package 2013 R3
- Ziel System Gadgeteer FEZ Spider und das Touch Display

Anforderung
Ziel soll eine Toucheingabe mit anschließend folgender Textausgabe "Button wurde gedrückt" für 3 Sekunden.

Wegen Zeitmangel vergessen
Vielleicht hatte ich zu viele Ideen und Baustellen, so dass ich vergessen habe, wie toll das System eigentlich ist. Mir fehlte zwar ein passendes Gehäuse, aber das sollte nicht hinderlich sein. Zunächst soll das System wieder zum Laufen gebracht werden.

Download Probleme
Wir leben im Jahr 2020, aber der Download wird bei GHI gehandelt, als hätten wir Anfang 2000 und DSL findet nach und nach seine Verbreitung in Deutschland. Viele Programme erfordern mehrere Gigabyte Daten für den Download, was inzwischen normal ist. Ärgerlich ist, wenn dies nicht nur langsam ist, sondern auch der Download abbricht oder fehlerhaft ist, und somit der Download von vorne anfangen muss. Damit der eine oder andere nicht ebenfalls diese Odyssey durchlaufen müssen, habe ich alle erforderlichen Inhalte als ZIP zusammengefasst.

Installation GHI Electronics NETMF SDK 2016 R1
Da sich mit der Zeit einige Links verändert haben, können zu einigen Inhalte die Daten nicht abgerufen werden. Am Besten in Advance, alles aushaken und nur das Ziel 'Framework' auswählen für die Installation.



Falls noch nicht installiert, dann auch "Install USB Drivers" mit anhaken.



Fehlendes Template
Nach den langen Downloads (insbesondere die 3,4GB netfm.zip) und Installation, kann zum Testen ein Beispielprogramm geschrieben werden. Visual Studio 2013 öffnen und neues Projekt erstellen. Pustekuchen, kein Template für Gadgeteer.




Template alternative
Nach der Suche im Internet und zahlreicher -nicht erfolgreicher- Installation, bzw. dem Prüfen, welche Installation nicht erfolgreich war, habe ich ein altes Projekt von mir genommen und daraus ein Template erstellt, das für den Gadgeteer FEZ Spider ausgelegt ist. Das war am Ende deutlich einfacher, als den Fehler in der Installation zu suchen.

Gadgeteer Template (Beinhaltet auch das Beispiel für das folgende Programm)

Die Datei "GadgeteerTemplate.zip" einfach in den Ordner "..\Dokumente\Visual Studio 2013\Templates\ProjectTemplates\Visual C#" einfügen. Anschließend kann dann Visual Studio 2013 gestartet werden und ein neues Projekt erstellt werden. Nun sollte auch das von mir erstellte Gadgeteer Template zu sehen sein.



Namen vergeben und dann wird die neue Solution erstellt. Das Template selbst kommt bereits mit der Beispiel-Anforderung und daher ist auch schon etwas Programmcode in der Program.cs vorhanden.

public partial class Program  
{  
     Text text;  
     void ProgramStarted()  
     {  
       CreateButton();  
     }  
     private void CreateButton()  
     {  
       var main = this.Display_T35.WPFWindow;  
       var canvas = new Panel();  
       canvas.HorizontalAlignment = HorizontalAlignment.Left;  
       canvas.SetMargin(4);  
       canvas.SetMargin(5, 5, 0, 0);  
       Rectangle rec = new Rectangle(200, 100);  
       rec.HorizontalAlignment = HorizontalAlignment.Left;  
       rec.VerticalAlignment = VerticalAlignment.Top;  
       rec.Fill = new SolidColorBrush(Colors.Green);  
       canvas.Children.Add(rec);  
       Font baseFont = Resources.GetFont(Resources.FontResources.NinaB);  
       text = new Text(baseFont, "Hello World!");  
       text.ForeColor = Colors.Black;  
       text.HorizontalAlignment = HorizontalAlignment.Left;  
       text.VerticalAlignment = VerticalAlignment.Top;  
       text.SetMargin(10, 10, 0, 0);  
       text.Height = 20;  
       text.Visibility = Visibility.Collapsed;  
       canvas.Children.Add(text);  
       main.Child = canvas;  
       main.TouchDown += new Microsoft.SPOT.Input.TouchEventHandler(main_TouchDown);  
     }  
     void main_TouchDown(object sender, Microsoft.SPOT.Input.TouchEventArgs e)  
     {  
       text.Visibility = Visibility.Visible;  
     }  
}


Verbindungen herstellen
Eins der super gemachten Dinge für die Entwicklung ist das Nutzen eines Designers, um die bestehenden Module per klicken und ziehen zu konfigurieren. Außerdem bekommt man so auch einen Plan, wie die vorliegende Hardware mit einander verbunden wird. Mit Doppelklick auf Program.gadgeteer öffnet sich die grafische Darstellung.



Rauf auf das Gadgeteer
Nach dem Anschließen des Gadgeteer sollte in den Eigenschaften des Projektes nun ein Gerät aufgelistet werden, das dann auch automatisch ausgewählt wurde.



Hello World!
Ist der Gadgeteer mit dem Display vorbereitet und mit dem PC per USB verbunden, kann das Beispielprogramm auf den Gadgeteer bereitgestellt werden. Und fertig.




Nicht aufgeben
Das Gadgeteer einstecken über USB Mini, erfolgte zunächst ohne ein Windows Sound. Keine Verbindung, vielleicht lag es mit USB 3.0 über dem HUB, was natürlich Unsinn war, dann eben mit einem anderen USB Mini Kabel, mit demselben Ergebnis. Und doch lag die fehlende Verbindung am USB. Zum Glück hatte ich den Kasten mit alten USB Kabeln noch nicht weg geworfen und fand noch ein USB Kabel mit einem USB Mini Anschluss und dieser funktionierte dann tatsächlich. Ich hatte davon schon mehrmals gehört und gelesen, aber noch nie selbst erlebt, seit es USB auf dem Markt gab.

Eigentlich ein schönes System
Das Gadgeteer FEZ Spider habe ich vor ungefähr fünf Jahren gekauft und nicht viel zum Experimentieren genutzt. Eigentlich schade, denn das System ermöglicht einen schnellen Einstieg für Software Entwicklung mit Hardware und man wird mit Hilfe des Designer auch geführt, die richtigen Verbindungen herzustellen. Alle Module die es dazu gibt, kommen mit einem eigenen Treiber, bzw. sind als Klassen verfügbar. Das Auseinandersetzen von Industrieschnittstellen ist daher für den Anfang nicht zwingend notwendig und erzielt durch das Plug&Play System schnell Ergebnisse.
Leider ist mir das Thema untergegangen und ich habe in der Vergangenheit noch nichts darüber geschrieben. Vielleicht kann ich das nachholen.

Ein paar Links:

Sonntag, 23. September 2018

PCA9685 PWM Driver Modul mit dem Netduino


Nach etwas längerer Zeit ist eine Thema mit dem Netduino wieder dran. Diesmal ist das PWM Treiber Modul dran, den ich schon etwas länger hier liegen habe und es ist eigentlich zunächst für den Raspberry Pi gedacht, aber da fehlt mir noch eine neue Platine um einen eigenen Motor Treiber zu bauen.

Benötigt wird:
  • Netduino (sollte mit allen Versionen funktionieren)
  • PCA9685 16 Channel PWM Driver (Meins ist nicht von Adafruit)
  • Mindestens ein Servo zum Testen
  • Externe Spannunsquelle mit Maximal 6V


Neues Projekt
Für das Projekt werden die Referenzen Microsoft.SPOT.Hardware und Microsoft.SPOT.Native die für die I²C und Debug Ausgaben verwendet werden. Neben der bestehenden Program.cs Klasse wird eine eigene Klasse für den PWM Treiber hinzugefügt, die dann den Inhalt hat des hier folgenden gezeigten Code. Weitere Kommentare sind im Github Repository zu sehen.

public class Pca9685 : I2CDevice
{
    private readonly byte PCA9685_MODE1 = 0x00;
    private readonly byte PCA9685_PRESCALE = 0xFE;

    private readonly byte LED0_ON_L = 0x06;
    private readonly byte LED0_ON_H = 0x07;

    private readonly byte LED0_OFF_L = 0x08;
    private readonly byte LED0_OFF_H = 0x09;

    private readonly byte ALL_LED_ON_L = 0xFA;
    private readonly byte ALL_LED_ON_H = 0xFB;
       
    private int _period;
       
    public Pca9685(int period) : base(new Configuration(0x40, 100))
    {
        this._period = period * 1000;
    }

    public void Reset()
    {
        this.Write(new byte[]{ PCA9685_MODE1, 0x00 });
        Thread.Sleep(10);
    }

    internal void Start()
    {
        int hz = 1000000 / this._period;

        this.Reset();
        this.SetPwmFrequency(hz);
    }
        
    public void SetPwmFrequency(float frequency)
    {
        frequency *= 0.9f;
        float prescaleval = 25000000;
        prescaleval /= 4096;
        prescaleval /= frequency;
        prescaleval -= 1;

        byte prescale = (byte)(prescaleval + 0.5);

        byte[] buffer = new byte[1] { PCA9685_MODE1 };
           
        if (this.Read(buffer) != 1)
        {
            throw new Exception("can not read mode1");
        }

        byte oldMode = buffer[0];

        byte newMode = (byte)(((byte)oldMode & (byte)0x7F) | (byte)0x10);

        // go to sleep
        this.Write(new byte[] { PCA9685_MODE1, newMode });
        this.Write(new byte[] { PCA9685_PRESCALE, prescale });
        this.Write(new byte[] { PCA9685_MODE1, oldMode });
        Thread.Sleep(5);
        this.Write(new byte[] { PCA9685_MODE1, (byte)(oldMode | 0x80) });

        byte[] bufferRead = new byte[] { PCA9685_MODE1 };
        this.Read(bufferRead);
        Debug.Print("Mode now: " + bufferRead[0].ToString());
    }
       
    internal void SetPwm(byte outputNumber, int on, int off)
    {
        Debug.Print("Output: " + outputNumber.ToString() + ", ON: " + on.ToString() + ", OFF: " + off.ToString());

        byte targetOutput_ON_L = (byte)(LED0_ON_L + 4 * outputNumber);
        byte targetOutput_ON_H = (byte)(LED0_ON_H + 4 * outputNumber);
        byte targetOutput_OFF_L = (byte)(LED0_OFF_L + 4 * outputNumber);
        byte targetOutput_OFF_H = (byte)(LED0_OFF_H + 4 * outputNumber);

        this.Write(new byte[] { targetOutput_ON_L, (byte)on });
        this.Write(new byte[] { targetOutput_ON_H, (byte)(on >> 8) });
        this.Write(new byte[] { targetOutput_OFF_L, (byte)(off) });
        this.Write(new byte[] { targetOutput_OFF_H, (byte)(off >> 8) });
    }

    private int Write(byte[] buffer)
    {
        I2CTransaction[] trans = new I2CTransaction[]
        {
            CreateWriteTransaction(buffer)
        };

        return Execute(trans, 1000);
    }
       
    private int Read(byte[] buffer)
    {
        I2CTransaction[] trans = new I2CTransaction[]
        {
            CreateReadTransaction(buffer)
        };

        return Execute(trans, 1000);
    }
}

Inhalt zur Klasse
Mit der Fertigstellung der Klasse, bzw. ist diese jetzt soweit ausgeführt, dass die einzelnen PWM Ausgänge des Moduls über die I²C gesteuert werden können. Sieht man in das Datenblatt, dann ist zusehen, dass das Modul noch ein paar mehr Sachen kann. Zum einen bin ich hier nicht auf das Kaskadieren des Moduls mit mehreren nicht eingegangen. Dazu komme ich auf ein anderes mal, wenn ich mehrere Module davon besitze.
Der Programmcode selbst habe ich ursprünglich aus öffentlichen C++ Programmcode entnommen und habe deshalb auch die selben Namen für Methoden verwendet wie aus dem Adafruit Programmcode.

Vielleicht verdreht
An der Stelle für SetPwm bin ich mir noch nicht sicher ob die Ansteuerung so richtig ist. Zugegeben, ich bin noch nicht mit dem Datenblatt durch. Da ich selbst weis, wie ein PWM Signal auszusehen hat, sollte auch die Ansteuerung meiner Vorstellung entsprechen. Doch wenn ich den High Puls festlege, dann ist dieser länger als der Low Puls Länge Inherhalb der Periode. Das sollte eigentlich umgekehrt sein.

Verkabeln
Mit vier Leitungen ist das Modul vom Netduino steuerbar. Damit aber auch die Servos später versorgt sind, muss in der Mitte ebenfalls eine Spannungsquelle angeschlossen werden.


Probleme
Falls ihr die Stromversorgung in der Mitte vom Modul mit dem 5V Ausgang vom Netduino versorgt, kann es sein, dass es nicht funktioniert oder instabil reagiert. Bedeutet, kann funktionieren, muss aber nicht. Besser ist immer eine seperate Versorgung anzuschließen. Allein je nach größe des verwendeten Servos und dessen Belastung, könnte die Stromaufnahme zu groß sein (Leider konnte ich keine Information finden, wieviel Strom maximal am 5V Pin abgenommen werden kann vom Netduino).

Die Wesentlichen Technischen Infos:
PWM Signal kann in 4096 Stufen gesteuert werden (12Bit)
PWM Signal kann zwischen 24Hz bis maximal 1526Hz eingestellt werden.
Die 16 Anschlüsse für die Stromversorgung der Servos, können maximal betrieben werden in Abhängigkeit der Angeschlossenen Spannungsversorgung.
PWM Output selbst kann maximal bis 25mA verwendet werden für LEDs
Spannungsversorgung für die Steuerung über I²C darf von 2,3V bis 5,5V liegen.
16 PWM Ausgänge
Kann über einen Externen Clock betrieben werden, der maximal bis 50MHz sein darf.
Intern ist ein 25MHz Ozillator verbaut.
Kann für LEDs oder Servos verwendet werden.


Quellenangaben:
Github Quellcode in C++
Datenblatt:



Sonntag, 11. Februar 2018

Motor Treiber für den Rover (.NETMF)


Was in C geht, geht auch in C#. Einige Zeit ist vergangen als ich das letzte Mal ein Beispiel zu Netduino gepostet habe. Viel hat sich nicht geändert und im Grunde verwendet ich statt .NETMF 4.2 nun .NETMF 4.3. Neu ist der Netduino 3 der in drei Varianten kommt und alle haben drei Go Port Anschlüsse. Zudem kommt dieser mit mehr Flash Speicher, bei den Varianten mit Netzwerk Eigenschaften. Nur der Takt ist weiterhin bei 168MHz, dass jedoch ausreichend ist für die meisten Anwendungen.

Nun zur Hardware
  • Netduino 3 Wifi
  • Motor Shield
  • Externe Spannungsversorgung mit 9V oder einen zwei Zellen Lipo
  • DAGU Rover 5 Chassis 4WD

Antrieb und Motor Shield
Zu meinem vorigen Post mit dem Arduino ändert sich am Antrieb und am Shield nichts. Die Pin Belegung und sowie auch die Betroffenen Signal Ausgänge, sind beim Netduino für diesen Zweck identisch. (Arduino, Motor Treiber für den Rover)

Programmcode
Mit der C# und .NET Micro Framework Variante habe ich weitgehend die Namen der Pin Variablen und auch die Methodennamen direkt übernommen. Ein Punkt ist jedoch anders. Das Ausgegebene Pwm Signal wird beim Initialisieren eingestellt. An der Stelle muss man sich bewusst machen, wie lang die Duration (die Länge des HIGH Level Puls) pro Periode sein darf. Beim Arduino konnte ein Wert von 0 bis 255 zugewiesen werden. Mit der PWM Klasse für den Netduino ist die einzustellende Duration davon abhängig, wie hoch die Periode gesetzt wurde. Für dieses Beispiel kann ein Duration Wert von 0 bis 1000 festgelegt werden.


 public class RoverMotorDriver  
 {  
   private OutputPort _latch;  
   private OutputPort _clock;  
   private OutputPort _data;  
   private OutputPort _regEn;  
   private PWM _outputM1;  
   private PWM _outputM2;  
   private PWM _outputM3;  
   private PWM _outputM4;  
   private const int _outputM1_A = 4;        
   private const int _outputM1_B = 8;        
   private const int _outputM2_A = 2;        
   private const int _outputM2_B = 16;       
   private const int _outputM3_A = 32;        
   private const int _outputM3_B = 128;       
   private const int _outputM4_A = 1;        
   private const int _outputM4_B = 64;        
   public RoverMotorDriver()  
   {  
     this._latch = new OutputPort(Pins.GPIO_PIN_D12, false);  
     this._clock = new OutputPort(Pins.GPIO_PIN_D4, false);  
     this._data = new OutputPort(Pins.GPIO_PIN_D8, false);  
     this._regEn = new OutputPort(Pins.GPIO_PIN_D7, false);  
     this._outputM1 = new PWM(PWMChannels.PWM_PIN_D11, 200d, .1d, false);  
     this._outputM1.Duration = 1;  
     this._outputM1.Start();  
     this._outputM2 = new PWM(PWMChannels.PWM_PIN_D3, 200d, .1d, false);  
     this._outputM2.Duration = 1;  
     this._outputM2.Start();  
     this._outputM3 = new PWM(PWMChannels.PWM_PIN_D6, 200d, .1d, false);  
     this._outputM3.Duration = 1;  
     this._outputM3.Start();  
     this._outputM4 = new PWM(PWMChannels.PWM_PIN_D5, 200d, .1d, false);  
     this._outputM4.Duration = 1;  
     this._outputM4.Start();  
   }  
   public void SetMotorShieldOn(bool setOn)  
   {  
     this._regEn.Write(!setOn);  
   }  
   public void MotorStop()  
   {  
     this.SetRunMotors(0, 0, 0);  
     this.SetMotorShieldOn(false);  
   }  
   public void SetForeward(int speedValueLeft, int speedValueRight)  
   {  
     int motorsOn = _outputM1_B | _outputM2_B | _outputM3_A | _outputM4_A;  
     this.SetRunMotors(motorsOn, speedValueLeft, speedValueRight);  
     this.SetMotorShieldOn(true);  
   }  
   public void SetBackward(int speedValueLeft, int speedValueRight)  
   {  
     int motorsOn = _outputM1_A | _outputM2_A | _outputM3_B | _outputM4_B;  
     this.SetRunMotors(motorsOn, speedValueLeft, speedValueRight);  
     this.SetMotorShieldOn(true);  
   }  
   public void SetTurnLeft(int speedValue)  
   {  
     int motorsOn = _outputM1_B | _outputM2_B | _outputM3_B | _outputM4_B;  
     SetRunMotors(motorsOn, speedValue, speedValue);  
     SetMotorShieldOn(true);  
   }  
   public void SetTurnRight(int speedValue)  
   {  
     int motorsOn = _outputM1_A | _outputM2_A | _outputM3_A | _outputM4_A;  
     SetRunMotors(motorsOn, speedValue, speedValue);  
     SetMotorShieldOn(true);  
   }  
   private void SetRunMotors(int motorsOn, int speedValueLeft, int speedValueRight)  
   {  
     this.SetOutputValue(speedValueLeft, speedValueRight);  
     this.RegisterWrite(motorsOn);  
   }  
   private void SetOutputValue(int speedValueLeft, int speedValueRight)  
   {  
     this._outputM1.Duration = this.GetLimitValue(speedValueLeft);  
     this._outputM2.Duration = this.GetLimitValue(speedValueLeft);  
     this._outputM3.Duration = this.GetLimitValue(speedValueRight);  
     this._outputM4.Duration = this.GetLimitValue(speedValueRight);  
   }  
   private uint GetLimitValue(int durationValue)  
   {  
     if(durationValue > 1000)  
     {  
       durationValue = 1000;  
     }  
     if(durationValue < 0)  
     {  
       durationValue = 0;  
     }  
     return 1000 + (uint)durationValue;  
   }  
   private void RegisterWrite(int motors)  
   {  
     this._latch.Write(false);  
     for (int bit = 0; bit < 8; bit++)  
     {  
       int target = motors & 0x80;  
       motors = motors << 1;  
       this._data.Write(target == 128);  
       this._clock.Write(true);  
       this.SleepWhile(5);  
       this._clock.Write(false);  
     }  
     this._latch.Write(true);  
   }  
   private void SleepWhile(int count)  
   {  
     for (int i = 0; i < count; i++) { }  
   }  
 }  

Unschönes
Die Methode "SleepWhile(int count)" ist eher Unschön, reicht aber hierfür aus, um den ShiftRegister richtig anzusteuern. Bisher traten mit der Einfachen Lösung keine Schwierigkeiten auf.

Fernsteuerung
Mit dem WLAN auf dem Netduino lässt sich schnell auch eine Fernsteuerung dazu bauen. Im nächsten Post werde ich darauf eingehen, wie man mit dem Netduino eine einfache Netzwerk Verbindung herstellt.



Dienstag, 13. Juni 2017

Netduino Netzwerk Adresse ändern


Seit langer Zeit hatte ich wieder einen Grund gefunden meinen Netduino aus zupacken und ein altes Projekt zu prüfen. Eigentlich suche ich eine Lösung, wie man eine Verbindung zum Netduino und einem SignalR Service herstellen kann. Zuvor hatte ich vor Jahren ein Beispiel mit WCF geschrieben, dass am Ende sogar relativ einfach war. Aber jetzt zum eigentlichen.

Netzwerk, Socket und Netduino
Mein Beispiel Projekt verwendete den SimpleSocket aus der Bibliothek Toolbox (https://netmftoolbox.codeplex.com/). Damit lässt sich in wenigen Codezeilen eine Verbindung zu einem anderen System über Netzwerk herstellen. Der Folgende Code Beispiel zeigt den Grundsätzlichen Aufbau einer Verbindung.

SimpleSocket socket = new IntegratedSocket(this._IpAddress, 80);
socket.Connect();
socket.Send("Hallo Welt");
socket.Close();


Das Problem
Die Verbindung blieb immer bei der Methode "socket.Connect()" stehen. Das kann dadurch festgestellt werden, wenn nach einer Weile nicht das gewünschte Ergebnis kommt und man dann die Pause Taste für das Debuggen klickt.



Ursache Prüfen
Meine Vermutung war, dass ich anscheinend nicht die richtige Adresse im Netduino Plus eingestellt hatte. Also startete ich aus dem Ordner "C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Tools" das Programm MFDeploy.exe. Über den Menü Punkt "Network" (Target > Configuration > Network) kann die Adresse eingerichtet werden.




Falsche Adresse
Wie ich bereits vermutet, hatte ich noch eine andere IP Adresse sowie auch eine andere Adresse für Gateway eingestellt. Nach dem ich die gewünschte Netzwerk Adresse eingetragen hatte, sollten die Einstellungen nach dem Click auf den "Update" Button auf den Netduino geschrieben werden. Doch dies wurde verhindert und ich erhielt einen Hinweis.




Lösung
Kommt diese Hinweismeldung "Unable to erase configuration sector memory", dann muss einmal der Befehl "Reboot and Stop" (Plug-in > Debug > Reboot and Stop) gesendet werden.



Bevor die Konfiguration für das Netzwerk wieder aufgerufen werden kann, musste ich auf die Nachricht "Reboot and Stop Complete" abwarten, die wiederum im unteren Rahmen angezeigt wurde.



Nun kann die Adresse geändert und erfolgreich auf dem Netduino geschrieben werden. Nach dem klicken "Update" sollte sich das Konfigurationsfenster schließen und im unteren Rahmen "Signature PASS" oder zumindest "Chk signatu" stehen.



Jetzt kann das Netduino Projekt mit dem SimpleSocket neu gestartet werden und die Verbindung mit der Ziel Adresse sollte hergestellt werden.

Dienstag, 14. Oktober 2014

Intervallaufnahme mit der Kamera ohne Interne Funktion

IntervallAufnahme01

Die Funktion für eine Intervallaufnahme ist simpel, daher ist es für mich unverständlich, warum nicht zumindest die Systemkameras von der Nikon 1 Serie in ihren Modellen diese Option anbieten. Nun gut, dies war kein Kaufkriterium, wäre aber schön gewesen. Ich selbst verwende die Nikon 1 J3 (die J1 hat die Intervallaufnahme), allerdings sollte mein Beispiel auch für andere Kameras funktionieren. Grundsätzlich geht es darum einen Servo auf den Auslöser zu drücken. Für diesen Zweck habe ich aus Aluminium Eckschienen eine Vorrichtung gebaut.

image

Wie schon am Bild zu erkennen ist, sollten zum Schutz die Alu Eckschienen nach der Verarbeitung abgerundet und ggf. mit Filzaufklebern beklebt werden.

Die Materialliste:

  • Alu Eckschienen ca. 20cm
  • Filzaufkleber
  • ein paar 3mm Schrauben und entsprechende Muttern
  • Mini Servo mit ca. 10 Ncm
  • Eine passende Schraube zur Kamera dass mit dem Stativ auch verschraubt werden kann. (siehe folgendes Bild)
image

Zum Ansteuern wird der Netduino mit einem passenden Shield zur Ansteuerung für einen Servo verwendet. Die geringe Belastung kann das Board verkraften. Bei Normalen und größeren Servos wird eine Externe Stromversorgung notwendig.

image

Der Aufwand des Programmcodes gestaltet sich übersichtlich. Zusehen ist, dass noch ein Analoger Wert eingelesen wird, worauf ich im nächsten Absatz zu sprechen komme.

public class Program
{
    // Für die Einstellung des Druckpunktes.
    private static SecretLabs.NETMF.Hardware.AnalogInput _Input;

    // Ansteuerung des Servos für den Auslöser.
    private static PWM _Servo;

    // Led Status wiedergeben.
    private static OutputPort _Status;

   // Einen Zähler.
    private static int _IntervallCounter = 0;

   // Nach wie viel Sekunden soll ausgelöst werden.
   private static int _IntervallEnd = 15;//30;

    public static void Main()
    {
        _Input = new SecretLabs.NETMF.Hardware.AnalogInput(Pins.GPIO_PIN_A0);
        _Servo = new PWM(Pins.GPIO_PIN_D5);
        _Status = new OutputPort(Pins.ONBOARD_LED, false);
           
        // 20ms für den Analog Servo.
        uint periode = 20000;

        // Mittelstellung bei 1,5ms
        _Servo.SetPulse(periode, 1500);

       while (true)
        {
           if (_IntervallCounter >= _IntervallEnd)
            {
                for (int i = 0; i < 3; i++)
                {
                    _Status.Write(true);
                    Thread.Sleep(100);
                    _Status.Write(false);
                    Thread.Sleep(100);
                }

                // Hinweis: bei Netduino 1 wird ein Wert gelesen von 0 bis 1023.
                //          Der Netduino 2 hingegen liest bis 4095.
                //uint duration = 1000 + (uint)_Input.Read();         // Ein kommentieren um den Servo
                //Debug.Print("Duration: " + duration.ToString());  // einzustellen am Pin A1.

                _Servo.SetPulse(periode, 1710); // 1710 ist eine Einstellung, an meiner Kamera
                Thread.Sleep(400);

               // Zähler Zurücksetzten.
                _IntervallCounter = 0;
            }
            else
            {
                // Aktivität zeigen
                _Status.Write(true);
                Thread.Sleep(100);
                _Status.Write(false);
               Thread.Sleep(900);

                _IntervallCounter++;
            }

           // Zurückstellen.
            _Servo.SetPulse(periode, 1500);
        }
    }
}

Das gleiche nochmal für den Arduino.

#include <Servo.h>
// Servo Kontrolle anlegen
Servo controlServo;
// Einstellung wie weit sich der Servo drehen darf
int pinAnalogIn = A0;
// Servo ansteuern
int pinServo = 5;
// LED Status
int pinLed = 13;
// Intervall Zähler
int intervall = 0;
// IntervallLänge in Sekunden
int intervallLength = 15; // 30;

void setup(){

  // Dient zu Einstellgunszwecke
  Serial.begin(9600);
  // Pins zuweisen
  pinMode(pinAnalogIn, INPUT);
  controlServo.attach(pinServo);
  pinMode(pinLed, OUTPUT);
}

void loop()
{
  if(intervall >= intervallLength)
  {
    for(int i = 0; i < 3; i++)
    {
      digitalWrite(pinLed, HIGH);
      delay(100);
      digitalWrite(pinLed, LOW);
      delay(100);
    }
   
   // Einkommentieren um die Reichweite des Servos einzustellen,
    // mit einem Potitometer am Pin A1.

    int result = 500; //analogRead(pinAnalogIn);
   
    controlServo.write(map(result, 0, 1023, 0, 179));
    delay(400);
   
    // Zähler zurück setzen
    intervall = 0;
  }
  else
  {
   digitalWrite(pinLed, HIGH);
   delay(100);
    digitalWrite(pinLed, LOW);
    delay(900);
    intervall++;
  }
 
  // Zurück stellen.
  controlServo.write(90);
}

Jetzt fehlt noch ein fester Wert für den Druckpunkt. Der Potentiometer wird an Pin A1 angeschlossen, die Spannungsversorgung muss beim Netduino mit 3,3V angelegt werden. Das Auslesen des Stellwertes erfolgt beim Netduino über Debug.Print() und wird über die Ausgabe angezeigt. Beim Arduino wird der Stellwert über dem Serial Monitor wiedergegeben. Je nach Belastung kann die Versorgung über USB instabil werden. Das zeigt sich dann, wenn der Servo beim Drücken des Auslösers leicht zittert. Später, mit einem 9V Akku oder Netzteil, sollten diese Schwierigkeiten nicht mehr auftauchen. Ansonsten muss der Servo einen eigenen Stromkreis erhalten, um zu verhindern, dass die Versorgung des Mikrocontrollers zu sehr schwankt und so zu einem Neustart des Programms führt.

image image
image image

Zu den Einstellungen der Kamera. Um ein schnelles Auslösen der Kamera zu ermöglichen, sollte auf den Kreativ Modus gewechselt werden. Die Blende, die Verschlusszeit und der Fokus sind am besten manuell festzulegen. Spätestens bei Nachtaufnahmen kommt man an diesen Einstellungen nicht vorbei. Achja, das Dateiformat JPG ist für die Ausgabe zu empfehlen. Mit dem NEF bzw. RAW ist das Bearbeiten im Video Schnittprogramm kaum machbar.

BeiNachtMitSternenhimmel_06

Tipp zur Nachtaufnahme: acht bis fünfzehn Sekunden für die Verschlusszeit bei maximal offener Blende reichen aus. Zu lange Belichtung führt dazu, dass die Sterne Streifen bilden, also praktisch gesehen wird die Erdrotation sichtbar. Die idealen Nächte mit Sternenhimmel sind in der Zeit um Neumond. Die besten Bedingungen sind Orte, die so dunkel sind, dass beim Hinabsehen die eigenen Füße nicht mehr zu sehen sind. Zwinkerndes Smiley

Nun noch ein paar Aufnahmen, die ich mit meiner Apparatur erstellt habe.

Zu guter Letzt auch noch der Quellcode zum Herunterladen.

Sonntag, 14. September 2014

Feuchtigkeitssensor HTU21D mit dem Netduino (Teil 2)

image_thumb1

Damit die gemessenen Werte auch brauchbare Messeinheiten haben, müssen die Rohdaten zunächst umgerechnet werden. Daher kommen zwei weitere Methoden hinzu, die dies erledigen. Der Inhalt zum Lesen des einzelnen Wertes erhält eine eigene Methode, weil dieser Inhalt zweimal verwendet wird und sich nur im einzelnen zusendenden Byte unterscheidet.

// Luftfeuchtigkeit Messen und Rohwert zurückgeben
public int Read()
{
    // Inhalt in eigene Methode umgezogen
}

// Liest mit der Übergabe des Befehls den Sensor Teil aus.
private int ReadSensorPart(byte command)
{
    // Befehl senden.
    if (this.Write(new byte[] { command }) == 0)
    {
        throw new SystemException("Fehler beim Senden!");
    }

    // Warten bis der Sensor mit dem Lesevorgang fertig ist.
    Thread.Sleep(50);

    byte[] data = new byte[3];

    // Messergebnis abrufen
    if (this.Read(data) == data.Length)
    {
        // Es werden für dieses Beispiel zwei Bytes verwendet
        int raw = (data[0] << 8) | data[1];
        raw &= 0xFFFC;
        return raw;
    }

    Debug.Print("Sensor konnte nicht gelesen werden!");
    return 0;
}

Damit wir die Daten zusammengefasst aus der Methode “Read” erhalten, wird das Ergebnis in einem Daten Objekt zusammengefasst. Daszu wird eine neue Klasse angelegt.

// Das Sensor Daten Objekt stellt die gemessenen Werte zur Verfügung.
public class SensorData
{
    // Ruft die gemessene Luftfeuchtigkeit ab.
    public float Humidity { get; private set; }

    // Ruft die gemessene Temperatur ab.
    public float Temperature { get; private set; }

   // Initialsiert das Daten Objekt mit den Rohwert Messungen
    // und rechnet diese auf bekannte Maßeinheiten.
    public SensorData(int rawHumidity, int rawTemperature)
    {
       
// Umrechnung aus den Rohdaten
    }

    // Überschriebene Methode für die Ausgabe der Berechneten Inhalte.
    public override string ToString()
    {
        return "Luftfeuchtigkeit: " + this.Humidity.ToString() +

                "% - Temperatur: " +     this.Temperature.ToString() + "°C";
    }
}

Im Datenblatt sind die Formel zu entnehmen für die Feuchtigkeit und Temperatur, die mit relativ wenig aufwand in einem Programmcode abgebildet werden können.

image
Feuchtigkeitswert = –6 + (125 * (Rohwert / 65536))

Formel für die Temperatur aus dem Rohwert berechnen.

image

Temperaturwert = –46,85 + (175,72 * (Rohwert / 65536))

Grundsätzlich benötigt die Umrechnung drei Werte. Das Summieren und Multiplizieren ist bei beiden Umrechnung die selbe. Diese Aufstellung lässt sich in eine Methode zusammenfassen und in die Klasse SensorData hinzufügen.

// Rechnet aus dem Rohwert das Zielergebnis um.
public float GetCalculateValueByRange(int raw, float start, float range)
{
    // Gemessenen Rohwert durch den Maximalen Ausgabewert.
   float f = raw / 65536f;

    // Messumfang multiplizieren und an den Startwert verschieben.
   return start + (range * f);
}

Nun kann die eigentliche Umrechnung im Konstruktor erfolgen.

...
// Initialsiert das Daten Objekt mit den Rohwert Messungen
// und rechnet diese auf bekannte Maßeinheiten.

public SensorData(int rawHumidity, int rawTemperature)
{
    // Berechnen der Luftfeuchtigkeit.
   this.Humidity = GetCalculateValueByRange(rawHumidity, -6f, 125f);

    // Berechnen der Temperatur.
    this.Temperature = GetCalculateValueByRange(rawTemperature, -46.85f, 175.72f);
}
...

Damit wäre das Daten Objekt mit der Umrechnung fertig. Jetzt muss in der Hauptklasse HTU21D der Lesevorgang vervollständigt werden.

...
// Luftfeuchtigkeit Messen und Rohwert zurückgeben

public SensorData Read()
{
   // Rohwert abholen für Luftfeuchtigkeit
    int rawHumidity = ReadSensorPart(0xf5);

    // Rohwert abholen für Temperatur
    int rawTemperature = ReadSensorPart(0xf3);

   // Neues Datenobjekt erzeugen und die Rohdaten übergeben.
    return new SensorData(rawHumidity, rawTemperature);
}
...

In der Program.cs beleibt noch eine kleine Anpassung und dann ist der Teil fertig.

public class Program
{
    public static void Main()
    {
        // Klasse Initialisieren und Verbindung herstellen.
        HTU21D sensor = new HTU21D();

        while (true)
        {
            // Sensoren lesen
            SensorData sd = sensor.Read();

           // Ergebnis ausgeben.
            Debug.Print(sd.ToString());
        }
    }
}

Vielleicht ist dem einen oder anderen aufgefallen, dass drei Bytes gelesen werden, aber nur zwei Bytes verwendet werden. Das Dritte beinhaltet den CRC Wert, mit dem sich prüfen lässt, ob das Ergebnis Valide ist. Darauf gehe ich dann im nächsten Post ein.

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...