Servomotor über serielle Konsole steuern

Hallo

Ich wollte meinen kleinen Servo mithilfe der seriellen Konsole von Arduino steuern.
Also flugs Servomotor angeschlossen und Programm geschrieben. Soweit keine Probleme, nur mein Programm funktioniert nicht so wie es soll.

#include <Servo.h>
Servo servo1;
int position = 0;


void setup(){
  Serial.begin(9600);
  servo1.attach(9);
}

void loop(){
  if (Serial.available()){
  position = Serial.read();
  servo1.write(position);
  Serial.print("Fahre auf ");
  Serial.print(position);
  Serial.print(" Grad");
  Serial.println("");
  }
  delay(100);
}

In der seriellen Konsole gebe ich 90 ein, aber als Ausgabe in der Konsole erscheint etwas ganz anderes

Fahre auf 57 Grad
Fahre auf 48 Grad

und der Servo fährt brav auf diese Winkel.

Aber wie kommen die zwei Winkel 57 und 48 Grad zustande, wenn ich einmal 90 eingebe?

Wenn ich hingegen einen Buchstaben eingebe, fährt der Servo auf die der ASCII-Nummer entsprechende Gradzahl. Bei z.B. A fährt der Servo auf 65 und bei a auf 97 Grad. Stimmt da was mit dem Datentyp Integer nicht?

Gruß
Atalanttore

Du hast das Problem schon korrekt erkannt. Dein Program liest wie folgt ein:

position = Serial.read();

Serial.read() liefert als Ergebnis Zeichen bzw. Bytes. Wenn Du sowas einer int Variablen zuweist, dann steht danach darin der ASCII Code des Zeichens.

Das ist alles so richtig. Deine Erwartungen sind falsch. Entweder Du benutzt das so oder Du musst den Input parsen. Ein guter Anfang ist das hier: Arduino Playground - CmdMessenger

@Atalanttore

... falls es funktioniert hat, kannst du bitte nochmal eine antwort posten, und eventuelle programmänderungen ?

würd mich interessieren ob es jetzt funktioniert.

grüssle

Huch, ganz schöner Brocken. Geht das nicht einfacher?

Gruß
Atalanttore

Aehm, das ist eine einfache Lösung. Noch einfacher ist es bei der bestehenden Lösung zu bleiben. D.h. Du kannst ja genausogut auf der PC Seite alles in ASCII Codes umrechnen. D.h. falls Du eh per Computer mit dem Arduino redest ist es fast immer einfacher dem Arduino die Daten "mundgerecht" zu liefern anstatt im Arduino sich zu verkünsteln.

Probier mal was in der Richtung:

void loop(){
  if (Serial.available()) { // Wenn ueberhaupt irgendwas zum Lesen ansteht
    position = 0; // Damit wir keine Vermischung mit alten Werten bekommen
    while (Serial.available()){ // Dann lies jedes Zeichen dass Du bekommen kannst
      position = position*10 + Serial.read()-'0'; // Und zwar den Zahlenwert, nicht ASCII, und verknuepfe es!
      delay(1); // Warte auf das ggf. naechste Zeichen
    } // Wenn nichts mehr kommt mach weiter
    servo1.write(position);
    Serial.print("Fahre auf ");
    Serial.print(position);
    Serial.print(" Grad");
    Serial.println("");
  }
}

Mit dem delay(1) solltest Du dem Computer genug Zeit zur Übertragung des nächsten Zeichens geben um nicht aus der while auszubrechen wenn er noch nicht fertig ist. Da er immer nur ein Zeichen (eine Zahl) einlesen kann musst Du die dann noch verknüpfen.

Die eingegebenen Zahlen stimmen mit der Änderung zwar mit der Ausgabe überein, aber immer noch werden die eingegebenen Zahlen meistens einzeln betrachtet.

10 wird zu 1 und 0 Grad. 150 hingegen wird zu 1 und 50 Grad.

Joghurt:

void loop(){

if (Serial.available()) { // Wenn ueberhaupt irgendwas zum Lesen ansteht
    position = 0; // Damit wir keine Vermischung mit alten Werten bekommen
    while (Serial.available()){ // Dann lies jedes Zeichen dass Du bekommen kannst
      position = position*10 + Serial.read()-'0'; // Und zwar den Zahlenwert, nicht ASCII, und verknuepfe es!
      delay(1); // Warte auf das ggf. naechste Zeichen
    } ...
  }
}

Was hat es mit diesem position*10 auf sich? Warum mal zehn?

Gruß
Atalanttore

Atalanttore:
Die eingegebenen Zahlen stimmen mit der Änderung zwar mit der Ausgabe überein, aber immer noch werden die eingegebenen Zahlen meistens einzeln betrachtet.

Vergrößer das delay() und probiers nochmal. 2 oder 3 sollten theoretisch reichen...

Atalanttore:
Was hat es mit diesem position*10 auf sich? Warum mal zehn?

Na Du willst doch die Zahlen zusammensetzen, und wenn Du z.B. 1, 5 und 0 zu 150 zusammensetzen willst, dann musst Du (1*10+5)*10+0 machen. Daher die 10.

Joghurt:

Atalanttore:
Die eingegebenen Zahlen stimmen mit der Änderung zwar mit der Ausgabe überein, aber immer noch werden die eingegebenen Zahlen meistens einzeln betrachtet.

Vergrößer das delay() und probiers nochmal. 2 oder 3 sollten theoretisch reichen...

Juhu, mit delay(3) funktionierts :slight_smile:

Joghurt:

Atalanttore:
Was hat es mit diesem position*10 auf sich? Warum mal zehn?

Na Du willst doch die Zahlen zusammensetzen, und wenn Du z.B. 1, 5 und 0 zu 150 zusammensetzen willst, dann musst Du (1*10+5)*10+0 machen. Daher die 10.

Ja schon. So habe ich das verstanden:

Der ASCII-Wert der eingegebenen Zahl wird eingelesen. Damit der ASCII-Wert zum Zahlenwert passt wird minus dem ASCII-Wert von '0' gerechnet. Dieses Ergebnis wird dann zu position*10 addiert.

Am Anfang ist position=0. Also position*10 auf jeden Fall auch 0. Der Wert von Serial.read()-'0' wird der neue Wert von position. Beim nächsten Durchlauf wird der neue Wert von position mit 10 multipliziert, damit die Zahl im Dezimalsystem zum Zehnerwert wird bzw. um eine Stelle nach links geschoben wird. Das ganze läuft solange durch bis keine eingegebenen Zahlen mehr da sind.

Gruß
Atalanttore

Genau. :slight_smile:

es geht auch so:

int incomingByte;
char Data[6];
int i;
unsigned int Tempo;

void setup()
{
Serial.begin(9600);
}

void loop(){

do {
if (Serial.available()) {
Data = Serial.read();

  • i++;*
  • } *
  • if(i<1)Tempo = millis();*
  • } while (i<5&&(millis()-Tempo) < 500);*

_ Data = 0; _
_
incomingByte = atoi(Data); _
_
i=0;
_
* Serial.println(incomingByte);*
}[/quote]
Der Kode erwartet bis zu 5 Ashii-Zahlen oder wartet 0,5 Sekunden und übersetzt dann den String in eine Integerzahl.
Dies ist nützlich, damit bei manueller Eingabe nicht führene Nullen eingegeben werden müssen.
Grüße Uwe