Posts mit dem Label Windows 10 IoT werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Windows 10 IoT werden angezeigt. Alle Posts anzeigen

Samstag, 13. Oktober 2018

PCA9685 PWM Driver Module mit Rasperry Pi & Win 10 Iot


Was mit dem Netduino geht, geht für gewöhnlich auch auf dem Raspberry Pi (wenn es nicht gerade um PWM Ausgänge geht). Grundsätzlich hatte ich das Modul tatsächlich für den Rasperry Pi gekauft, um die Fehlende Ausgabemöglichkeit eines PWM Signal auszugleichen. Zwar kann man einen Pin so programmieren, dass ein PWM Signal erzeugt wird, aber ich fand dieses Lösung zunächst nicht sehr ansprechend.

Benötigt:
  • Raspberry Pi 2 oder 3
  • 8GB SD Karte mit installierten Windows 10 IoT
  • Mindestens ein Servo zum Testen
  • Externe Spannungsquelle mit Maximal 6V


UWP Anwendung erstellen
Nam dem anlegen einer neuen Solution wird für den Zugriff auf die Schnittstelle I²C die Reference "Windows IoT Extensions for the UWP" hinzugefügt. Das Beispiel geht mit allen Version die für Windows 10 IoT und Raspberry Pi.


Fast identisch
Außer der Zugriff auf das Interface für I²C, ist der Aufruf gleich. Das Konfigurieren der Schnittstelle erfolgt z.B. nicht im Konstruktor, sondern in einer eigenen Initialisierungsmethode. Grund hierfür ist das abrufen der Instanz von I2cDevice das über die DeviceInformation abgeholt werden kann.

internal class Pca9685 {

private readonly byte _address = 0x40;
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 I2cDevice _i2cDevice;

public Pca9685(int period) : base()
{
    this._period = period * 1000;
}

private async Task Init()
{
    var i2cSettting = new I2cConnectionSettings(this._address);
    i2cSettting.BusSpeed = I2cBusSpeed.StandardMode;

    var deviceSelector = I2cDevice.GetDeviceSelector();

    var deviceInfo = await DeviceInformation.FindAllAsync(deviceSelector);
    this._i2cDevice = await I2cDevice.FromIdAsync(deviceInfo[0].Id, i2cSettting);

    if (this._i2cDevice == null)
    {
        throw new Exception("i2cDevice is null");
    }
}
…

Zudem sind alle Methodenaufrufe Asynchron. Die Lösung ist auch in Synchron möglich, jedoch denke ich, dass die Verwendung von Async, Await und Task noch im Übersichtlichen Rahmen ist.
Allerdings muss man sich bewusst machen, obwohl die Methoden Asynchron ausgeführt werden können, müssen die Aufrufe nacheinander erfolgen. Überschneiden oder gleichzeitig ist Technisch für die Schnittstelle nicht möglich, weil die Bits nacheinander geschrieben werden können.

…
public async Task Reset()
{
    if(!this.Write(new byte[] { this.PCA9685_MODE1, 0x00 }))
    {
        throw new Exception("Can not send the reset command.");
    }

    await Task.Delay(10);
}

internal async Task Start()
{
    await this.Init();

    int hz = 1000000 / this._period;

    await this.Reset();
    await this.SetPwmFrequency(hz);
}

public async Task SetPwmFrequency(float frequency)
{
    byte prescale = this.GetPrescale(frequency);

    byte[] buffer = new byte[1] { this.PCA9685_MODE1 };

    if (!this.Read(buffer))
    {
        throw new Exception("can not read mode1");
    }

    byte oldMode = buffer[0];

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

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

    byte[] bufferRead = new byte[] { this.PCA9685_MODE1 };
    this.Read(bufferRead);
}
        
internal void SetPwm(byte outputNumber, int on, int off)
{
    byte targetOutput_ON_L = (byte)(this.LED0_ON_L + 4 * outputNumber);
    byte targetOutput_ON_H = (byte)(this.LED0_ON_H + 4 * outputNumber);
    byte targetOutput_OFF_L = (byte)(this.LED0_OFF_L + 4 * outputNumber);
    byte targetOutput_OFF_H = (byte)(this.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) });
}
…

Zur Vollständigkeit kommen noch die restlichen Methoden. Das Umrechnen (GetPrescale) ist hier direkt kopiert und unterscheidet sich auch nicht mal von der C++ Version die in den Sourcecode von Adafruit fand.
Das Versenden der Bytes braucht nicht mehr code als die Netduino Lösung.

…
private byte GetPrescale(float frequency)
{
    frequency *= 0.9f;
    // internal clock frequency
    float prescaleval = 25000000;
    prescaleval /= 4096;
    prescaleval /= frequency;
    prescaleval -= 1;

    return (byte)(prescaleval + 0.5);
}

private bool Write(byte[] buffer)
{
    var result = this._i2cDevice.WritePartial(buffer);

    if (result.Status != I2cTransferStatus.FullTransfer)
    {
        Debug.WriteLine(result.Status);
    }

    return result.Status == I2cTransferStatus.FullTransfer;
}

private bool Read(byte[] buffer)
{
    var result = this._i2cDevice.ReadPartial(buffer);

    if (result.Status != I2cTransferStatus.FullTransfer)
    {
        Debug.WriteLine(result.Status);
    }

    return result.Status == I2cTransferStatus.FullTransfer;
}
}

Kommt mir immer noch verdreht vor
Bereits im Blog Post für das Netduino Beispiel, hatte ich den Eindruck, dass die Ansteuerung verdreht ist. Das Verhalten ist auch hier Identisch.

Verkabeln
Vier Leitungen reichen, um das Modul Anzusteuern. Hier sind keine Besonderheiten. Damit der Angeschlossene Servo zuverlässig reagiert, sollte eine Externe Spannungsquelle angeschlossen werden.

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 Oszillator verbaut.
Kann für LEDs oder Servos verwendet werden.


Github Quellcode in C++
Datenblatt:


Sonntag, 3. Juni 2018

Windows 10 Iot Core Dashboard Installation geblockt


Da will man wieder über das Dashboard seinen Raspberry Pi erreichen, erscheint eine Meldung, dass die Anwendung blockiert wurde. Für mich verwunderlich, dass etwas von Microsoft nicht mehr ausgeführt oder installiert werden konnte.

Ursache
Nach einem Größeren Update für Windows 10 im Mai, wurde die Sicherheitseinstellung für das Installieren über URL ausgeschaltet. Um das wieder frei zu geben, muss lediglich eine Einstellung in der Registrie geändert werden.

Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\Security\TrustManager\PromptingLevel\Internet = Enable


Die Änderung funktioniert akut, so dass die Installation des Windows 10 IoT Dashboard im Anschluss gestartet werden kann. Statt einer Meldung, dass die Installation geblockt wird, sollte nun die Option Installieren wieder verfügbar sein.


Zuletzt
Hier sollte man sich Bewusst machen, dass mit der Freigabe eine mögliche Sicherheitslücke für "Man in the middle attack" geöffnet wird. Unschön aber offensichtlich notwendig.

Donnerstag, 26. April 2018

SQLite oder CSV auf Raspberry Pi 2/3 mit Win 10 IoT


Irgendwann kommt der Punkt, da möchte man seine Daten auch speichern. Bei dem Einsatz von vielen Daten kann auf die Klassische Art in einer CSV im IsolatedStorage gespeichert werden. Ist einfach zu lesen führt aber zu redundante Dateninhalte. Mit SQLite lassen sich relationale Dateninhalte zusammenstellen. Aber man muss zusätzliche Referenzen hinzufügen und sich mit SQL auseinandersetzen (allerdings nur ein wenig). Beide Varianten funktionieren auf PC, Tablet, Windows Phone und natürlich auf Raspberry Pi 2 und 3.

Was nehme ich?
Vorweg sollte man sich fragen, was wird mein Ziel. Das hängt immer von der eigenen Anwendung ab. Soll in der Stunde ein Durchschnittswert errechnet werden der dann angezeigt werden soll, dann reicht sicherlich ein Array.
Möchte ich Benutzereinstellungen Speichern? Dann könnte der folgende Programmschnipsel reichen der den IsolatedStorage verwendet.

ApplicationData.Current.LocalSettings.Values["MyKey"] = MyValue;
if (ApplicationData.Current.LocalSettings.Values.ContainsKey("MyKey"))
   
MyValue = ApplicationData.Current.LocalSettings.Values["MyKey"];

Aus <https://stackoverflow.com/questions/42750736/uwp-how-to-use-isolated-storage>

Für diesen Bleiben wir zunächst bei einfachen Datensätzen, die einfach hintereinander gespeichert und in einer Tabelle abgebildet werden können. Der Teil für Relationale Datenhaltung wird auf den nächsten Post eingegangen.

Datenabruf
Der Vorteil einer Datenbank Abfrage ergibt sich dann beim Abrufen der Daten. Wurden z.B. so viele Daten gespeichert, dass es in Summe ca. 20 Megabyte groß ist, dann könnte sich das Öffnen der CSV Datei etwas lang werden. Und wenn man nur zu einem Bestimmten Zeitraum oder einen Bereich haben möchte, dann ist das gesamte einlesen einer Datei und das Parsen der Inhalte relative Zeitintensiv.

Performance Vergleich?
Hierzu muss man sagen, dass der IsolatedStorage nicht gedacht ist, schnell einzelne Daten zu speichern. In der Demo Anwendung sind zwei provisorische Klasse mit IsolatedStorage angelegt. Diese sind nur soweit geschrieben, das damit Lesen, Speichern und zurück setzen ermöglicht. (siehe am Ende Link zum Github Repository). Letzten Endes sollte anhand der Ergebnisse zeigen, was für den einen oder anderen die bessere Entscheidung sein könnte und unabhängig von weiteren Kriterien betrachtet werden sollte.

Test Vorgang
Als erstes wird festgelegt, wie viele Daten geschrieben werden. Vorbelegt als Default Anzahl sind zehn Datensätze zu erstellen. Die Werte sind in allen Datensätzen dieselben, außer der letzte wird abwechselnd zu gewiesen (hier Wohnzimmer und Flur).


Jeder Vorgang wird zehnmal ausgeführt und gemessen. Daraus ergibt sich dann die Durschnittszeit.

  • Bestehende Daten löschen
  • Zehn Mal die Anzahl Daten schreiben. Bei jeden neu lauf, werden die Daten wieder gelöscht.
  • Die Zehn Daten, zehnmal lesen. Daten werden immer neu geladen.
  • Datenabrufen und auf "Wohnzimmer" filtern. Auch hier werden die Daten immer neu geladen

 Nach dem Durchlauf werden die Ergebnisse der einzelnen Durchläufe sowie der Durchschnittswert angezeigt.

Beispiel Anwendung
Die Beispiel UWP Anwendung ist nur Zweckmäßig für den Test aufgebaut. Oben Links kann über die Textbox ein Wert ab 1 eingetragen werden und setzt damit die Anzahl Daten, die geschrieben werden sollen. Mit dem Button "Run" wird der Test Ausgeführt. Sobald dieser durchlaufen ist, steht unter dem Button "Finish".
Rechts sind zwei Textboxen die wiederum zwei Beispiele zu SQLite und IsolateStorage abbilden, die im Programmcode im Einzelnen betrachtet werden können. (Die Ergebnisse im Bild können von PC zu PC abweichen)


Ergebnisse
Ziel Systeme sind natürlich der Raspberry Pi 2 und 3. Warum das Schreiben mit SQLite auf dem Raspberry Pi3 langsamer ist, konnte ich mit dem Testaufbau noch nicht ermittelt. Zudem kommt, dass nach dem Aufräumen der Methoden Inhalte, sich die Zeiten verschlechtert haben. Warum dies ist, werde ich auf einen späteren Post eingehen.

Ergebnisse in dem der Programmcode einfach runter geschrieben wurde


Ergebnisse nach dem Aufräumen


Fazit
Der Aufbau der Methoden kann sicherlich besser gelöst werden. Die Methoden Inhalte in 'Func<T>' auszulagern ließ die Ergebnisse deutlich mehr schwanken. Die Gemessene Zeit stieg um das Sieben- bis Zehnfache an beim Lesen mit 'Where' Abfragen. Der Test mit Schreiben in die SQLite Datenbank ist dahingegen weniger abweichend, dafür aber beim IsolatedStorage ist die Durschnittszeit auf ca. das zwanzigfache angestiegen.
Beide haben Vor- und Nachteile. Mit diesen Beispiel Test wäre IsolatedStorage gut fürs schnelle Speichern und für das schnelle Lesen die SQLite Datenbank.
Die Auslegung der Tests ist sehr spärlich und behandelt noch nicht das Verwenden von Relationalen Daten. Das kommt dann mit den nächsten Posts.

Offene Punkte:
  • Speichern von Relationalen Daten
  • Abruf von Relationalen Daten
  • Probleme mit Code Optimierung




Referenzen

Wieviel grad zum Ausweichen

Das Thema Ausweichen ist für nicht ganz so leicht, wenn man nur feststellen kann, dass ein Objekt vor einem liegt oder nicht. Ich ...