Go Down

Topic: Arduino mit Processing steuern (Read 5493 times) previous topic - next topic

-Jonas-

Hallo zusammen :)

in meinem Projekt, würde ich gerne mehrere Motoren, von Processing aus steuern. Das Ziel war eigentlich, dass ich nur Variabeln von Processing zum Arduino schicke, und dass dann der Arduino den Sketch selbst hat, um zu wissen, was mit den Daten anfangen.

nun bin ich aber daran gescheitert. Ich habe keine Ahnung, was an meinem Sketch nicht stimmt:

(zum Testen habe ich einfach mal 2 leds genommen)

Arduino Code:

Code: [Select]
char incomming;
int incommingCounter = 0;
int arrayCounter = 0;
const char d = ',';
char *pch;
const char *delimiters = &d;

int led1;
int led2;

int led1P = 13;
int led2P = 12;

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

  pinMode(led1P, OUTPUT);
  pinMode(led2P, OUTPUT);
}

void readSerial()
{
  int bufferSize;
  char incommingBuffer[bufferSize];
  char *data = &incommingBuffer[bufferSize];
  while(Serial.available())
  {
    incomming = Serial.read();
    if(incomming != '\r')
    {
      bufferSize = Serial.readBytesUntil(d, incommingBuffer, k);
    }
    else
    {
      pch = strtok(data, delimiters);
      while (data != NULL)
      {
        value[arrayCounter++] = atoi(pch);
        pch = strtok (NULL,  delimiters);
      }
     
      led1 = value[1];
      led2 = value[2];
    }
  }
}

void loop ()
{
  if (led1 == 111)
  {
    digitalWrite(led1P, HIGH);
  }
  else if (led1 !=111)
  {
    digitalWrite(led1P, LOW);
  }
  if (led2 = 222)
  {
    digitalWrite(led2P, HIGH);
  }
  else if (led2 != 222)
  {
    digitalWrite(led2P, LOW);
  }
}


Processing Code:
Code: [Select]
import processing.serial.*;
Serial myPort;

void setup()
{
  size (500, 500);
  println(Serial.list());
  myPort = new Serial(this, "/dev/tty.usbserial-A9007U0O", 9600);
}

void draw()
{
  int led1 = 111;
  int led2 = 222;
 
  myPort.write(str(led1));
  myPort.write(",");
  myPort.write(str(led2));
  myPort.write(",");
  myPort.write("\r");
 
  delay(1000);
}


ich wäre sehr dankbar, wenn sich jemand den code schnell durchschauen könnte.

Gruss Jonas

Serenifly

#1
May 22, 2014, 04:33 pm Last Edit: May 22, 2014, 05:29 pm by Serenifly Reason: 1
Deine Einlese-Funktion kann nicht funktionieren. Du machst da mehrere katastrophale Fehler

Code: [Select]

 int bufferSize;
 char incommingBuffer[bufferSize];
 char *data = &incommingBuffer[bufferSize];


1.) bufferSize ist undefiniert. Das muss zur Compile-Zeit feststehen!
2.) die dritte Zeile ist völlig unnötig. Die Variable für den Puffer ist schon ein char* !! In C sind Arrays Zeiger auf das erste Element.
3.) readSerial() wird nirgends aufgerufen was ich so sehe
4.) Dein String wird nicht mit NULL terminiert
5.) Du entfernst mit Serial.read() schon Zeichen aus dem Eingangspuffer. Diese kannst du danach nicht mit readBytesUntil() einlesen, da sie schon weg sind.
6.) Selbst wenn es gehen würde, liest readBytesUntil nur bis zum Komma. Bei strtrok() willst du aber den ganzen String auf einmal einlesen und danach splitten. Das ist eine Hybrid-Version zwischen den zwei Möglichkeiten, die beides nicht richtig macht.

Dann ein weiteres großes Problem:
die serielle Übertragung ist langsam und dauert bei 9600 Baud ca. 1ms pro Zeichen! Du kannst also nie while(Serial.available() machen). Das liest ein Zeichen ein und bricht dann ab weil der Rest noch unterwegs ist.


Mach es so:
Code: [Select]

const int SERIAL_BUFFER_SIZE = 31;
char serialBuffer[SERIAL_BUFFER_SIZE];

void loop()
{
  if(readSerial() == true)
  {
  }
}

bool readSerial()
{
static byte index;

if(Serial.available() > 0)
{
char c = Serial.read();

if(c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
{
serialBuffer[index++] = c;
}
else if(c == '\r')
{
serialBuffer[index] = '\0';
index = 0;
return true;
}
}
return false;
}


Und Teste erst mal ob die Zeile ganz ankommt, bevor du sie mit strtok() bearbeitest.

Das ruft die Funktion ständig auf und schaut ob was da ist. Es ist also völlig egal wie lange die Übertragung dauert. Wenn die Funktion dann true zurück gibt ist sie fertig und man kann den String verarbeiten


Und strtok() will einen String an Delimitern. Also:
Code: [Select]
pch = strtok(serialBuffer, ",");
Hier wieder: Strings sind Zeiger auf das erste Element und damit schon ein char*

Und "incoming" schreibt man mit einem "m" ;)

-Jonas-

hey!

Zuerst mal riiiesen Dank für deine Hifle. ich weiss, dass ich sozusagen keine Ahnung vom programmieren habe, und desshalb habe ich ja auch fehler erwartet.. ;)

das ist jetzt vielleicht noch eine dümmere Frage: aber wie soll ich testen, ob die Zeile ganz angekommen ist, wenn die serielle Schnittstelle ja schon besetzt ist..? Dann kann ich ja nicht " Serial.print()" kommen...


Dann habe ich noch ein weiteres Probelm, was mit dem i2C bus zu tun hat: ich kann mein Gerät (ein Druck- und Temperatursensor) nicht mit dem klassischen Wire-master_reader auslesen..

Vielleicht weiss ja jemand auch hier auf die schnelle, was das für ein Problem ist. Im Anhang befindet sich das Datenblatt mit rechts auf der 2. Seite dem Kommunikationsbeschrieb.

Der Code fürs Arduino ist der gewohnte:
Code: [Select]
#include <Wire.h>

void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop()
{
  Wire.requestFrom(0x40, 6);    // request 6 bytes from slave device #2

  while(Wire.available())    // slave may send less than requested
  {
    char c = Wire.read(); // receive a byte as character
    Serial.print(c);         // print the character
  }

  delay(500);
}


Vielen Dank für die Hilfe

Jonas



Serenifly


das ist jetzt vielleicht noch eine dümmere Frage: aber wie soll ich testen, ob die Zeile ganz angekommen ist, wenn die serielle Schnittstelle ja schon besetzt ist..? Dann kann ich ja nicht " Serial.print()" kommen...

Doch kannst du. Empfangen und Senden sind zwei getrennte Leitungen. Daher kannst du einfach Serial.println(serialBuffer) machen.

Am besten du lässt erst mal Processing weg und verwendest den normalen Serial Monitor. Tippe etwas ein und schaue was der Arduino zurück sendet.

Quote
Vielleicht weiss ja jemand auch hier auf die schnelle, was das für ein Problem ist. Im Anhang befindet sich das Datenblatt mit rechts auf der 2. Seite dem Kommunikationsbeschrieb.

Und dein Code machst was völlig anderes.

Eine Messung wird glaube ich angefordert wenn du das machst:
Code: [Select]

Wire.beginTransmission(ADR);
Wire.write(0xAC);
Wire.endTransmission();


Dann fehlen die 6ms Wartezeit. Und erst dann kannst du die Daten mit requestFrom() und read() auslesen. Und zwar 5 Bytes, (1 Status, 2 Druck, 2 Temp). Nicht 6.

So 100%ig sicher bin ich mir dabei aber auch nicht.

-Jonas-

Danke für die richtig schnelle Antwort! :)

Aber was macht denn der Code, der das Beispiel zeigt..? ich dachte das sei das :~

ich probier das gleich mal alles aus.. :)

Serenifly

Dein Code liest einfach 6 Bytes ein. Aber du musst erst mal eine Messung anfordern, dann warten (oder komplizierter das Status Byte auslesen oder die Status Leitung abfragen) und dann das Ergebnis auslesen.

-Jonas-

Ich habe noch mal am Kommunikationszeugs mit Processing herumgebastelt (bzw. eingefügt was du mir gesagt hasst XD)

jetzt habe ich aber noch das Problem, dass ich, wenn ich den delay() im void draw() von Processing unter 500 setze, funktioniert gar nichts mehr.. weshalb ist denn das so?!

hier die Codes:
Processing:
Code: [Select]
import processing.serial.*;
Serial myPort;

int firstValue, secondValue;



void setup()
{
 size (500, 500);
 println(Serial.list());
 myPort = new Serial(this, "/dev/tty.usbmodem1411", 9600);
}

void draw()
{
 background(51);
 
 int led1 = mouseX;
 int led2 = mouseY;
 
 myPort.write(str(led1));
 myPort.write(",");
 myPort.write(str(led2));
 myPort.write(",");
 myPort.write("\r");
 
 text(firstValue, 10, 30);
 text(secondValue, 10, 60);
 
 delay(500);
}

void serialEvent(Serial myPort)
{
 if (myPort.available() > 0)
 {
   String completeString = myPort.readStringUntil(10);
   if (completeString != null)
   {
     trim(completeString);
     String seperateValues[] = split(completeString, ',');
     firstValue = int(seperateValues[0]);
     secondValue = int(seperateValues[1]);
   }
 }
}


und der Arduino:
Code: [Select]
const int SERIAL_BUFFER_SIZE = 31;
char serialBuffer[SERIAL_BUFFER_SIZE];

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

void loop()
{
  if(readSerial() == true)
  {
    Serial.print(serialBuffer);
    Serial.print(',');
    Serial.println();
  }
}

bool readSerial()
{
 static byte index;

 if(Serial.available() > 0)
 {
   char c = Serial.read();

   if(c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
   {
     serialBuffer[index++] = c;
   }
   else if(c == '\r')
   {
     serialBuffer[index] = '\0';
     index = 0;
     return true;
   }
 }
 return false;
}


wäre mal wieder Dankbar um Hilfe XD
LG

-Jonas-


Dein Code liest einfach 6 Bytes ein. Aber du musst erst mal eine Messung anfordern, dann warten (oder komplizierter das Status Byte auslesen oder die Status Leitung abfragen) und dann das Ergebnis auslesen.


Ahhh okk ja ich seh's

Danke :D

Serenifly

Setze vielleicht mal die Baudrate auf 115200. Aber auch bei 9600 sollte eigentlich Zeit sein um etwas mehr zu senden.

-Jonas-

Dann funktioniert gar nichts mehr  :~ :D

-Jonas-

Sonst hat hier niemand eine Idee, was das Problem sein könnte, komm' da echt gerade nicht mehr weiter...  :~

LG

Serenifly

Kann ich so jetzt auch nicht sagen.

Da du sowieso Probleme mit dem Programmieren des Protokolls hast (auch wenn es erst mal funktionieren) würde, wäre Firmata eine Option:
http://playground.arduino.cc/Interfacing/Processing

Damit kann man den Arduino direkt vom PC aus fernsteuern. Das Protokoll wird einem schon abgenommen und man sagt auf dem PC "setzt diesen Ausgang" etc.

-Jonas-

ja das habe ich mir auch schon angeschaut. Das geht aber nicht, da der Arduino autonom bleiben muss. Es muss die Möglichkeit geben, ein "Notfallsystem" zu programmieren, dass in Gang gesetzt wird, wenn der Arduino über eine gewisse Zeit nicht mehr mit dem PC verbunden ist.

Serenifly

#13
Jun 06, 2014, 04:01 pm Last Edit: Jun 06, 2014, 06:43 pm by Serenifly Reason: 1
Was du auch probieren kannst ist auf die ganze String Geschichte zu verzichten und direkt Binärdaten zu senden.

Du kannst Werte von 0-255 in einem Byte schicken. Für einen 16 Bit Integer (auf dem PC short, nicht int!) müsstest du also den Integer in High-Byte und Low-Byte aufteilen:
Code: [Select]

short data = 1234;
byte highByte = (byte) (data & 0xff);
byte lowByte = (byte) ((data >>> 8) & 0xff);


Auf Arduino Seite:
Code: [Select]

int data = word(highByte, lowByte);


Warnung:
Java ist Big Endian! Dadurch ist die Byte-Ordnung anders herum als auf der Arduino Seite.

EDIT:
Aber wenn man es auf beiden Seiten per Hand macht, sollte das keine Rolle spielen. :) Wenn man aber irgendwo Konverter Klassen von Integer zu Byte Array o.ä. hat, dann muss man aufpassen.


Auf dem Weg kann man auch Kommandos schicken, z.B. im ersten Byte. Oder ein Byte als Längenfeld verwenden wenn man eine Nutzlast variabler Länge hat.

Wenn du erst mal nur 4 Bytes schickst, kannst du einfach auf Serial.available() >= 4 abfragen und direkt vier mal read() machen.

michael_x

Quote
Du kannst Werte von 0-255 in einem Byte schicken. Für einen 16 Bit Integer (auf dem PC short, nicht int!) müsstest du also den Integer in High-Byte und Low-Byte aufteilen:
Code: [Select]
short data = 1234;
byte highByte = (byte) (data & 0xff);
byte lowByte = (byte) ((data >>> 8) & 0xff);
Warnung:
Java ist Big Endian! Dadurch ist die Byte-Ordnung anders herum als auf der Arduino Seite.

Da hast du dich leider etwas verguckt. Innerhalb von Java sollte es gar nicht interessieren, weil es da keine Pointer gibt, die man missbrauchen kann. Wo es drauf ankommt (NIO Buffer etc), kann man rauskriegen oder einstellen, auf was für Hardware Java gerade läuft.
Nach meinem Verständnis ist das Low Byte  das niederwertigste Byte, wo immer es im Speicher stehen mag.
Code: [Select]
short data = 0xAFFE;
byte lowByte = (byte) (data & 0xff);  // ergibt 0xFE
byte hiByte   = (byte) ((data >> 8) & 0xff);  // ergibt 0xAF


In welcher Reihenfolge Binär-Daten übertragen werden, muss man natürlich festlegen, wenn man nicht nur Einzel-Bytes überträgt.

Der Fluch, wenn man zwar arabische Zahlen verwendet, Texte aber weiter falschrum (links nach rechts) schreibt ;)

Go Up