AdSense

Montag, 24. Juni 2013

Quadrocopter - Erste Auftriebsmessung mit Motor und ESC

(English version) Ich habe heute einmal den Auftrieb von einem bei Ebay gekauften Motor und Propeller getestet. Es handelt sich dabei um diesen Motor (Der Preis ist vermutlich falsch, ich habe 9,75 gezahlt) mit diesem Steuergerät. Dazu habe ich noch einen 10x4,5 Propeller benutzt (welcher zwar eigentlich nicht zum Motor passt, aber ich hatte den gerade zur Hand). Das Fazit: 238 Gramm Auftrieb.

Die Spezifikationen des Motors sind: 1200 kV, 25 A maximal, 18 A kontinuierlich. Die des Steuergerätes sind: 25 A maximal, 20 A kontinuierlich. Da ich davor keinerlei Ahnung hatte, wie viel was ausmacht, habe ich nun grobe Werte die sagen, mit wie viel Auftrieb man rechnen kann.

Meine Messung sieht folgendermaßen aus: Der Motor ist auf einem Gestell aus Lego auf einer Waage befestigt. Die Geschwindigkeit wird über einen ATmega eingestellt (siehe dazu ATmega - ESC ansteuern), welcher ein Potentiometer ausliest und dementsprechend die Geschwindigkeit einstellt. Die Messanordnung sieht folgendermaßen aus:

Sonntag, 23. Juni 2013

SPI-Kommunikation zwischen ATmega und Raspberry PI

(English version) Ich messe die Temperatur in meinem Zimmer (siehe dazu ATmega - Temperaturmessung mit dem DS18S20). Diese liest ja ein ATmega aus. Die Temperaturdaten vom ATmega werden dann per SPI (siehe dazu ATmega - Hardware-SPI) an meinen Raspberry PI gesendet, welcher die Daten sammelt, mittelt und plottet (Gnuplot). Das fehlende Glied in der Kette ist jetzt die Kommunikation zwischen ATmega und Raspberry PI. Nach einigem recherchieren habe ich eine recht gut und schnell funktionierende Lösung gefunden. Auf dem Raspberry PI benutze ich die Library bcm2835 (http://www.airspayce.com/mikem/bcm2835/). Diese hat eingebaute SPI-Befehle.

Auf der Raspberry PI-Seite sieht das folgendermaßen aus (ich gehe davon aus, dass man sich mit SPI etwas auskennt):

#include <bcm2835.h>
int main (int atgc, char** argv)
{
  if (!bcm2835_init())
  {
    return 1;
  }
  bcm2835_spi_begin();
  bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST);
  bcm2835_spi_setDataMode(BCM2835_SPI_MODE0);
  //CLOCK_DIVIDER: Kann verschieden gesetzt werden, hier gibts es mehr dazu
  bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_65536);
  bcm2835_spi_chipSelect(BCM2835_SPI_CS0);
  bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW);

  //Ein Beispiel, welches sendBits sendet und danach returnBits empfängt.
  char sendBits = 0b11111111;
  char returnBits = 0;

  while(true)
  {
    bcm2835_spi_transfer(sendBits);
    //Man kann theoretisch gleichzeitig senden und empfangen, 

    //ich sende aber lieber zuerst
    //und schicke dann einfach Dummy-Bits (0b00000000)
    returnBits = bcm2835_spi_transfer(0);

  }
}


Auf der ATmega-Seite sieht das folgendermaßen aus (die drei SPI-Funktionen von dem ATmega - Hardware-SPI Post müssen natürlich im Prorgamm sein):

#include <avr/io.h>
int main()
{
  SPI_SlaveInit();
  char returnBits = 0;
  char sendBits = 0b11111111;
  while(true)
  {
    data = SPI_SlaveReceive();
    SPI_SlaveSend(sendBits);
  }
}

Mittwoch, 19. Juni 2013

C# - Punkt oder Komma Problem

(English version) Früher, in C war das einfach. Das Komma im deutschen wird einfach als Punkt eingegeben, fertig. Jeder Programmierer wusste "Alle Zahlen werden englisch eingetippt, also mit Punkt".

Tja... C# denkt, intelligent zu sein und akzeptiert, je nach Region, das Komma statt dem Punkt. Das sieht bei mir zum Beispiel folgendermaßen aus:

string stringGerman = "3,1415926";
string stringEnglish = "3.1415926";
double doubleGerman = Convert.ToDouble(stringGerman);
double doubleEnglish = Convert.ToDouble(stringEnglish);
System.Console.Write("{0} - {1}\n", doubleGerman, doubleEnglish);


Die Ausgabe ist: 3,1415926 - 31415926, er akzeptiert also den Punkt nicht. Dies hat schon öfters zu Verwirrung geführt, da man ja instinktiv Zahlen mit Punkt eingibt (wenn sie als Usereingabe zuerst ein string sind). Die Lösung ist zum Glück ziemlich simpel:

double doubleEnglish = Convert.ToDouble(stringEnglish, CultureInfo.InvariantCulture);

Damit sagt man dem Compiler, dass er die Schreibweise mit Punkt akzeptieren soll. Fügt man das bei beiden Convert.ToDouble() ein, so liest er den englischen string richtig (und den deutschen natürlich falsch).

Damit wäre die Richtung von string zu double also erledigt. Kommen wir zur Rückrichtung. Bei mir liefert eine .ToString()-Methode immer die Komma-Schreibweise. Will ich also Daten für Gnuplot oder ähnliches mit C# abspeichern, so klappt das nicht. Die Lösung ist hier aber auch ziemlich simpel. Man muss einfach der .ToString()-Methode den selben CultureInfo-Parameter übergeben wie der Convert.ToDouble()-Methode:

string numberEnglish = doubleVariable.ToString(CultureInfo.InvariantCulture);

Dienstag, 18. Juni 2013

Android - Notizzettel fürs Handy

(English version) Da mir des öfteren unterwegs etwas wichtiges einfällt, was ich mir unbedingt notieren muss, jedoch fast nie Zettel und Stift dabei habe, habe ich mir eine App für Android geschrieben, welche als Notizzettel funktioniert. Zusätzlich dazu habe ich auf dem Handy Dropbox und DropSync installiert, diese Apps (insbesondere DropSync) synchronisieren dann die Notizen in meine Dropbox in einen Ordner namens toSync.

Das ganze funktioniert folgendermaßen: DropSync kann einzelne Ordner synchronisieren. Auf dem Handy ist daher ein Ordner "toSync" auf der SD-Karte, welchen DropSync automatisch synchronisiert. Meine App greift auf diese Datei zu.

Am Computer kann man dann die Datei notes.dat in der Dropbox einsehen bzw. bearbeiten. Für Linux hatte ich auch mal eine Anzeigesoftware geschrieben, das werde ich wohl in den nächsten paar Wochen auch für Windows umsetzen, dann stell ich das hier auch rein. Im allgemeinen ist das ja nicht schwer, das muss lediglich ein Fenster mit einem Textfeld und einem Speichern-Knopf sein.

Die App kann man sich hier herunterladen. Zum funktionieren muss allerdings der Ordner "toSync" vorhanden sein und auch die Datei "Notes.dat", sonst kann es sein, dass die App abstürzt.

Montag, 17. Juni 2013

C# - DirectSound

(English version) Für Panzerkampf habe ich Sounds gebracht. Die standard-Sound-Abspielmethode ist allerdings nicht in der Lage, mehrere Sounds gleichzeitig zu spielen (der laufende Sound bricht ab, wenn ein neuer gestartet wird). Daher habe ich DirectSound von DirectX installieren müssen. Dafür braucht man erstmal das DirextX SDK.

Vor dem Installieren sollte man "Microsoft Visual C++ 2010 Redistributable" deinstallieren, sonst tritt (jedenfall bei mir) der Fehler "S1023" auf (hier gibt es mehr dazu).

Danach kann man einfach eine Referenz sein C#-Projekt hinzufügen (Durchsuchen - Microsoft.DirectX.DirectSound, bei mir lag die Datei in C:\Windows\assembly\GAC\Microsoft.DirectX.DirectSound).

Außerdem muss in App.config noch etwas verändert werden. Davor sah es da bei mir so aus:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>


<startup> muss verändert werden zu <startup useLegacyV2RuntimeActivationPolicy="true">, sodass das ganze dann so aussieht:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup useLegacyV2RuntimeActivationPolicy="true">
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>


Der Code um Sounds abzuspielen sieht dann folgendermaßen aus:

Device applicationDevice = new Device();
applicationDevice.SetCooperativeLevel(this, CooperativeLevel.Priority );

soundBuffer1 = new SecondaryBuffer( "c:\afile1.wav", applicationDevice );
soundBuffer2 = new SecondaryBuffer( "c:\afile2.wav", applicationDevice );

soundBuffer1.Play( 0, BufferPlayFlags.Looping );
soundBuffer2.Play( 0, BufferPlayFlags.Looping );


Freitag, 14. Juni 2013

C# Windows Forms - DoubleBuffer zeichnen

(English version) Für Panzerkampf muss ich ja die Panzer und alles zeichnen. Da Windows Forms grundsätzlich mal alles löscht und dann neu zeichnet, kann es zu starkem Flackern führen. Um das zu verhindern, lasse ich im Hintergrund immer alles in ein Bitmap zeichnen und zeichne das dann auf die aktuelle Ebene - welche ich nicht lösche. Dadurch ist das Problem mit dem Flackern komplett behoben. Hier der Code dazu:

void drawDoubleBufBmp()
{
    Bitmap localBitmap = new Bitmap(settings.SizeX, settings.SizeY);
    using (Graphics gx = Graphics.FromImage(localBitmap))
    {
        gx.DrawDrawString("Panz.....");
    }
    lock (BmpLock)
    {
        doubleBufBmp = localBitmap;
    }
}


private Bitmap doubleBufBmp = new Bitmap(settings.SizeX, settings.SizeY);
private object BmpLock = new object();
protected override void OnPaint(PaintEventArgs e)
{
    lock (BmpLock)
    {
        g.DrawImage(doubleBufBmp, 0, 0);
    }
}

protected override void OnPaintBackground(PaintEventArgs e)
{}


OnPaintBackground habe ich einfach überschrieben, dies hat zu dem Flackern geführt. In der OnPaint Funktion male ich dann einfach das fertige doubleBufBmp in das Fenster rein. drawDoubleBufBmp() wird bei mir von einem Thread alle 10 Millisekunden aufgerufen, welcher danach this.Invalidate(); aufruft, was dafür sorgt, dass die OnPaint-Funktion aufgerufen wird.

Montag, 10. Juni 2013

Minecraft Server - Spielerposition herausfinden mit C#

(English version) Ich habe einen kleinen (privaten) Minecraft-Server am laufen. Da es notwendig wurde, habe ich begonnen, einen Karte zu programmieren, welche alle Spieler an ihren Positionen anzeigt.

Die Positionen sind auf derm Server unter /world/players/<name>.dat abgespeichert und werden etwa alle 45 Sekunden aktualisiert. Das ist zwar nicht sehr häufig aber für eine grobe Karte reicht das.

Um die .dat Dateien zu lesen benutze ich Library "LibNbt", https://github.com/aphistic/libnbt, da die Dateien nicht im Klartext abgespeichert werden. Nach dem Download entpackt man das Ganze einfach, bindet das LibNbt Projekt in das eigene C# Projekt ein und macht eine Referenz drauf, mehr ist da nicht zu tun.

Um dann schließlich die Positionen auszulesen gehe ich folgendermaßen vor:

DirectoryInfo d = new DirectoryInfo(@"world\players\");
var file = new NbtFile();
//Eine Datei, in welche die Positionen gespeichert werden
StreamWriter outFile = new StreamWriter("positions.dat");
string lines = "";
//Alle Spieler durchlaufen
foreach (var file1 in d.GetFiles("*.dat"))
{
    //Spieler-Datei laden
    file.LoadFile(d + file1.Name);
    NbtCompound root = file.RootTag;
    int i = 0;
    //Alle Tags durchlaufen, bis das Positions-Tag gefunden wird
    foreach (NbtTag tag in root.Tags)
    {
        if (tag.Name.Equals("Pos"))
        {
            NbtList posList = ((NbtList)root[i]);
            //Als string abspeichern
            lines += file1.Name.Substring(0,file1.Name.Length-4)

                                  + "\t"
                  + ((NbtDouble)posList[0]).Value
                                  + "\t"
                  + ((NbtDouble)posList[2]).Value
                                  + "\r\n";
        }
        i++;
    }

}
outFile.WriteLine(lines);
outFile.Close();

Um das alles auf einer Karte anzuzeigen habe ich mit dem Programm "AMIDST" einen Screenshot meiner Welt gemacht (von -4096, -3072 bis 4096, 3072) und rechnte die Spielerpositionen dann in Pixel um und male dann mit einem anderen Programm, welches nur zum Anzeigen da ist, Kreuze und Namen drauf.