Arduino mit ruby ansteuern

Date: 31. August 2016 Posted by: owerner In: Informatik, Physik

Im Lehrplan Informatik des Landes Schleswig-Holstein ist für den zwölften Jahrgang die Thematik „physical Computing“ vorgesehen.
Da ich schon einige gute Erfahrungen mit dem Arduino-System gemacht habe, möchte ich dies auf jeden Fall verwenden. Dazu die Sunfounder-Sensorbox die für wenig Geld eine große Auswahl verschiedenster Sensoren enthält, die man nicht Löten muss – denn es ist ja Informatik und nicht Physik oder Elektrotechnik.
Die ArduinoIDE ist zwar ganz nützlich – aber ich scheue mich davor, bei 20 SuS die fehlenden Semicolons und Curlys zu ergänzen.

Da meine Schule nicht so große Neigung zeigt, in meine Leibsprache LabVIEW zu investieren (Campuzlizenz!), habe ich mal im Internet herumgesucht, ob man nicht auch mit ruby Arduinos ansteuern kann.
Ja, man kann!

Man muss lediglich die passenden gems nachinstallieren.

Die ersten Versuche waren frustrierend – man weiß ja nicht, wonach man nun genau sucht.

Erste Erfolge hatte ich mit einem Buch: „Getting Started with Arduino and Ruby“ von Agus Kurniawan.
Es ist etwas langatmig mit allen Installationsschritten für Linux und Windows – aber bei einem Preis von unter 10€ für das eBook kann man nicht meckern.

Er beschreibt die ersten Schritte mit den gems „serialport“ und „Firmata“.
„Serialport“ ist ganz nett, man kann digital lesen/schreiben und analog lesen.

„Firmata“ liefert zusätzlich einen Sysex-Systemaufruf, in den man die Bibliotheken der Sensoren einbauen kann.
Meine Internetrecherche hat ergeben, dass Firmata wohl an das MIDI-Protokoll angelehnt ist, mit dem Computer mit Keyboards (also diese Musik-Dinger) kommunizieren.
Firmata hat große Ähnlichkeiten mit dem LINX-Protokoll, was eignen Befehle betrifft.
Natürlich muss der Arduino während des laufenden Programms per USB an den steuernden Rechner angeschlossen bleiben – es wird kein StandAlone-Code erzeugt.
Als Fernziel wäre ja vielleicht mal eine 3d-Drucker-Steuerung mit LabVIEW oder ruby denkbar…

Das Buch beschreibt mit Firmata:

  • digital lesen/schreiben
  • PWM-Steuerung
  • analog lesen
  • Servo ansteuern

Allerdings nicht den, für mich interessanten Sysex Systemaufruf.

Sehr hilfreich war diese Seite.

Ich habe nun als erstes den PIN 13 Blink-Test gemacht. Dazu habe ich dieses Ruby-Beispiel verwendet.

Da ich ruby 2.2 verwende, konnte ich die ersten drei Zeilen des Quelltextes weglassen.
Mit der ArduinoIDE habe ich nun die passsende Firmware geladen und getestet und schon lief alles nach Plan.

Soweit die Pflicht – nun die Kür:

Von einem anderen Projekt habe ich noch einen DHT11 Sensor angeschlossen, der die relative Luftfeuchtigkeit und die Temperatur auslesen kann.

Die Firmware habe ich aus dem Codebeispiel für den Sensor gebastelt:

#include <Firmata.h>
#include <dht.h>
 
const int DHT11_PIN= 10;
dht myDHT;
 
void setup()
{
  Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION);
  Firmata.attach(START_SYSEX, sysexCallback);
  Firmata.begin(57600);
}
 
void loop()
{
  while(Firmata.available()) {
    Firmata.processInput();
  }
}
 
void sysexCallback(byte command, byte argc, byte*argv)
{
  switch(command){
  case 0x01: // LED Blink Command
    if(argc < 3) break;
    byte blink_pin;
    byte blink_count;
    int delayTime;
    blink_pin = argv[0];
    blink_count = argv[1];
    delayTime = argv[2] * 100;
 
    pinMode(blink_pin, OUTPUT);
    byte i;
    for(i = 0; i < blink_count; i++){
      digitalWrite(blink_pin, true);
      delay(delayTime);
      digitalWrite(blink_pin, false);
      delay(delayTime);
    }
    Firmata.sendSysex(command, argc, argv); // callback
    break;
  case 0x02: //DHT auslesen
    int chk = myDHT.read11(DHT11_PIN);
    int myTemp=int(myDHT.temperature*10);
    int myHum=int(myDHT.humidity*10);
    byte sign=0;
    if (myTemp<0)
      {sign=1;
       myTemp=myTemp*(-1);
      }
    argc=6;
    argv[0]=sign;
    argv[1]=lowByte(myTemp);
    argv[2]=highByte(myTemp);
    argv[3]=1;
    argv[4]=lowByte(myHum);
    argv[5]=highByte(myHum);
    //argv[0]=1;
    //argv[1]=2;
    //argv[2]=3;
    //argv[3]=4;
    //argv[4]=5;
    //argv[5]=6;
    delay(200);
    Firmata.sendSysex(command, argc, argv);   
  break;
  }
}

Und hier der kommentierte ruby-code:

require 'arduino_firmata'
 
arduino = ArduinoFirmata.connect 'COM5'
puts "firmata version #{arduino.version}"
 
## regist event
 
arduino.on :sysex do |command, data|
  puts "command : #{command}"                     #gibt die Nummer des Kommandos aus
  puts "data    : #{data.inspect}"                #hier wird das Rückgabearray ausgegeben
  # argc ist dabei die Länge -> muss in der Firmware gesetzt werden.
  # Das Array wird mit doppelter Länge zurückgegeben, d.h nur jeder zweite Wert ist ein echter Wert!
  # die Temperatur und Luftfeuchtigkeit müssen als globale Variablen deklariert werden, damit aus diesem "Objekt" weiter gereicht werden können.
  $myTemp=(data[2]+256*data[4])/10.0              #Temperatur wird auf eine Nachkommastelle genau ausgelesen.
  if (data[0]==1) then                             #Für negative Temperaturen wird der erste Wert auf 1 gesetzt.
    $myTemp=-1.0*$myTemp
  end
  $myHum=(data[8]+256*data[10])/10.0
end
## send sysex command
while true do  
  13.times do puts()                              #Das löscht den Bildschirm Zahl muss bei anderen Terminalgrößen geändert werden.
    end    #löscht das Terminalfenster
  arduino.sysex 0x02, [1, 2, 3, 4, 5, 6]          #der eigentliche Funktionsaufruf das array ist nur ein Platzhalter
 ## myTemp=(data[1]+256*data[2])*0.1
 puts("Temperatur: " + $myTemp.to_s+"  Grad C")   
 puts("Luftfeuchte: " + $myHum.to_s+ " Prozent")
 sleep(2)                                         # 2 Sekunden warten -> die Firmware wartet 200 ms. Wenn man zu häufig ausliest, schwingen die Werte sehr stark.
end

Es hat eine ganze Zeit gedauert, bis ich das Auslesen in den Griff bekommen hatte, da das Rückgabearray die doppelte Länge hat und nur jeder zweite Wert geschrieben wird. Man kann in der Firmware noch die auskommentierten Zeilen der ersten Tests sehen, die mich auf die richtige Idee gebracht haben.
Insgesamt finde ich meine Vorgehensweise etwas unelegant… workarround und globale Variabeln…

Aber da der Code seinen Dienst tut, bin ich für’s Erste zufrieden.

2 thoughts on “Arduino mit ruby ansteuern

  • Frank sagt:

    Danke für den Tipp!

    Wie sieht es mit Performance und Speicherbedarf aus?

    Ich fürchte bei der systemnahen Programmierung hat C immer noch seine Berechtigung, auch in Ruby kommt man ja nicht um Bit- und Bytegefummel herum und handelt sich ggf. noch Probleme ein, die aus der Nutzung von C Libraries aus einer anderen Sprache entstehen. Ich erinnere mich noch mit Grausen daran andersherum aus C heraus Fortran-Libraries zu nutzen …

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.