Kommunikation- Arduino und PC

Hallo zusammen,

beschäftige mich seit kurzem mit dem Arduino und bekomme die Kommunikation zwischen Arduino und PC nicht so richtig hin.
Zum Einstieg baue ich die Aufgaben/ Tutorials von der Webseite www.mr.intermediadesign.de nach. Bei Kapitel 3.1.3 Aufgabe 6 komme ich leider nicht weiter.
Auf der Webseite steht das einfach bei dem Arduino Code und in dem Processing Code jeweils ein delay von 100 gesetzt wird.
Bei mir funktioniert dies aber nicht. Bei einem delay von 100 passiert bei den LED's einfach nichts. Erst wenn ich im Processing Code ein delay von 900 setzte, wird das Signal scheinbar richtig von dem Arduino erkannt.

Habe ich vielleicht einen typischen Fehler gemacht? Etwas nicht eingestellt?

Benutze den Arduino Mega. Im folgenden mal mein Code, da ich ihn nicht ganz so habe wie auf der genannten Webseite.
Processing

import processing.serial.*;

Serial myPort;

int ANZAHL_LEDS = 8;
Rect[] rectangle;
int size = 20;
int curid;

void setup()
{
  size(350,130);
  println(Serial.list());
  String portName = Serial.list()[1];
  myPort = new Serial(this, portName, 9600);
  
  rectangle = new Rect[ANZAHL_LEDS+1];

for(int id=1;id<9;id++){
  rectangle[id] = new Rect(20+(id*30),20,id);   
}
}

 int i = 0;
void draw()
{

  for(int i=1;i<9;i++){
  rectangle[i].drawRect();
  }
  println(curid);
  myPort.write(curid);
  delay(100); //Wenn ich hier den Delay höher 900 setzte funktioniert es (Anwendung reagiert dann logischerweise aber auch langsam)


  
}

void mousePressed()
{
  for(int i=1;i<rectangle.length;i++)
  {
    rectangle[i].toogle();
  }
}

//Klasse Rect
class Rect
{
int x;
int y;
int id;

boolean istInnerhalb = false;

//Konstruktor
Rect(int x, int y, int id)
{
  this.x=x;
  this.y=y;
  this.id=id;
}
void drawRect()
{
  if(istInnerhalb)
  {
    fill(0,0,255);
    rect(x,y,size,size);
  }
  else
  {
    fill(255,255,255);
    rect(x,y,size,size);
  }
}
void toogle()
{
  if(dist(mouseX,mouseY,x+10,y+10)<(size/2))
  {
    istInnerhalb =! istInnerhalb;
    if(istInnerhalb==true){
      curid = this.id;
    }else{
      curid = 0;
    }
  }
}
}

Arduino-Code

const int clockPin = 3;
const int latchPin = 4;
const int dataPin = 5;

void setup(){
  pinMode(clockPin,OUTPUT);
  pinMode(latchPin,OUTPUT);
  pinMode(dataPin,OUTPUT);
  Serial.begin(9600);
}
int input = 0;
void loop(){
    if(Serial.available())
    {
    input = Serial.read();
    Serial.print(input);
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, input);  
    digitalWrite(latchPin, HIGH); 
    }
    delay(100);

}

Ich hoffe mir kann dabei jemand einen Tipp geben. Vielen Dank schonmal.

Grüße

Edit: Auch der Quellcode 1:1 von der Webseite funktioniert bei mir erst ab einem Delay von 900.

Crasher92:
Habe ich vielleicht einen typischen Fehler gemacht? Etwas nicht eingestellt?

Ich habe zwar keinen blassen Schimmer, was Du da machst, aber ich verstehe, dass es sich um ein Problem mit der seriellen Kommunikation handelt. Daher versuche ich mal, die serielle Kommunikation Deiner beiden Programme zu verstehen und beschränke mich rein darauf.

Processing:

int curid;
...
myPort.write(curid);
delay(100);

Du sendest also eine Integer-Variable, so wie ich das sehe also zwei Bytes (High-Byte und Low-Byte, des Integers) alle 100 Millisekunden im Höchstfalle (kann ich nicht genau erkennen, wie oft tatsächlich).
Das mit den Integer / 2 Bytes entnehme ich so jedenfalls der Beschreibung des write-Befehls von Processing hier:

Arduino:

int input = 0;
void loop(){
    if(Serial.available())
    {
    input = Serial.read();
    ...
    }
    delay(100);
}

Der Arduino-Code liest mit Serial.read immer nur ein Byte zur Zeit ein, und zwar höchstens alle 100 Millisekunden.

Da sehe ich zwei Problemfelder:
Gesendet werden Integer-Werte (je 2 Bytes), empfangen werden Byte-Werte (je ein Byte, wird dann an ein Integer zugewiesen)

Und wenn das beides alle 100 Millisekunden passiert, dann läuft einerseits der Arduino-Eingangspuffer (64 Bytes Größe) nach 6,4 Sekunden über (in 6,4 Sekunden werden 64 Integers = 128 Bytes gesendet, aber nur 64 Bytes empfangen).

Das Überlaufen des seriellen Eingangspuffers am Arduino ist das eine. Das wird verhindert, indem die loop am Arduino MINDESTENS so oft durchläuft, wie Zeichen an der Schnittstelle eintreffen. Wenn Du also mit Prozessing 2 Bytes in 100 Millisekunden sendest, würde ich das delay beim Arduino auf DEUTLICH UNTER 50 Millisekunden setzen.

Also in der Arduino-loop: delay(25);
Wenn das delay dort überhaupt sein muß, normalerweise kann das doch komplett raus dort?

Das zweite sind die unterschiedlichen Datentypen: Integer-Werte mit Processing senden, Byte-Werte mit Arduino empfangen. Dabei wäre zu prüfen, ob tatsächlich Integer-Werte oder Byte-Werte übertragen werden sollen. Wenn ein übertragener Wertebereich von 0 bis 255 ausreicht, dann sollen ja wohl vermutlich Bytes über die Leitung gehen. In dem Fall würde vermutlich die Processing-Deklaration:
int curid;
auf
byte curid;
geändert werden müssen.

Wie gesagt, ich weiß nicht, was Deine Programme machen, ich habe mir nur mal die serielle Kommunikation angeschaut und was da mit welchem Timing über die Leitung geht.

Wenn Du Dir nicht sicher bist, was genau in welcher Datenrate ankommt, logge es einfach mal mit. Anbei mal ein Sketch, der zwei Sekunden lang Datenverkehr mitschneidet und ausgibt, und zwar von 1s nach Programmstart bis 3s nach Programmstart. Im Zweifelsfall mal reinschauen, ob das, was von Processing kommt, auch das ist, was erwartet wird.

Testprogramm für 2-Sekunden-Serial-Mitschnitt, ASCII-Codes als Dezimalzahl ausgeben:

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  Serial.println("Daten, die in 2 Sekunden eintreffen");
}

// the loop routine runs over and over again forever:
void loop() {
  byte b;
  while (millis()<1000) 
  {
   if  (Serial.available()) Serial.read();
  } 
  while (millis()<3000)
  {
    if (Serial.available())
    {
      b=Serial.read();
      Serial.println(b);
    }
  }  
}

Hallo,

vielen Dank für deine Antwort. Hatte auf die Datentypen garnicht geachtet.

Das Programm macht auch nicht viel sinn :wink: Es war einfach zum Testen. Habe es hinbekommen, allerding muss ich im Arduino Code immer erst einen einmaligen Delay von knapp 1 Sekunde machen. Sonst starten meine Programme wohl nicht richtig.

Habe jedoch noch eine neue Frage, die auch mehr oder weniger zur Kommunikation gehört.

Neue Frage:

Ich übertrage Daten vom Typ "binary" und möchte unterscheiden wie die Daten verarbeitet werden sollen.
Beispiel: Ich übertrage zweimal eine binäre 1. Möchte aber das unterschiedliche Dinge passieren.

Ich brauche also soetwas wie eine "Flag", die ich vor den eigentlichen Daten setzten kann.
Kann mir dazu jemand einen guten Umsetzungsvorschlag machen?
Mein eigener Lösungsansatz wäre: Zwei bestimmte Bits definieren, wenn diese Bits dann empfangen werden, wird je nach Fall anders verarbeitet. Allerdings stoße ich dann irgendwann an meine maximal 255 verfügbaren Möglichkeiten und müsste schon 2 Bits senden.

Falls jemand dazu einen guten Vorschlag hat, würde ich mich über Rückmeldung freuen.

Dankeschön schonmal :wink:

Crasher92:
Mein eigener Lösungsansatz wäre: Zwei bestimmte Bits definieren, wenn diese Bits dann empfangen werden, wird je nach Fall anders verarbeitet. Allerdings stoße ich dann irgendwann an meine maximal 255 verfügbaren Möglichkeiten und müsste schon 2 Bits senden.

Die kleinste softwaretechnisch über Serial empfangbare Einheit ist das Byte (nicht das Bit). Also empfängst Du mit jedem read() immer acht Bit auf einmal.

Jedes Byte kann einen Code von 0b00000000 bis 0b11111111 als Bitmuster enthalten, das sind 256 verschiedene Möglichkeiten der Codierung (Dezimal 0 bis 255).

Was Du codierst, ist rein Dir selbst überlassen. Wenn Du nur 0 und 1 übertragen möchtest, die je nach Fall unterschiedlich verarbeitet werden sollen, kannst Du es ganz problemlos in einem Byte codieren. Sagen wir mal das rechte Bit 0 oder 1 ist die übertragene Info, und das linke Bit im Byte is der Fall A oder B, dann sendest Du:
Fall A, Bit 0: 0b00000000
Fall A, Bit 1: 0b00000001
Fall B, Bit 0: 0b10000000
Fall B, Bit 1: 0b10000001

Wenn Du das Byte empfangen hast, dann stellst Du einfach fest, ob das linke Bit gesetzt ist und erhältst Fall-A oder Fall-B, und durch Abfrage des rechten Bits im Byte erhältst Du 0 oder 1.

Crasher92:
Ich übertrage Daten vom Typ "binary" und möchte unterscheiden wie die Daten verarbeitet werden sollen.
Beispiel: Ich übertrage zweimal eine binäre 1. Möchte aber das unterschiedliche Dinge passieren.

Ich brauche also soetwas wie eine "Flag", die ich vor den eigentlichen Daten setzten kann.
Kann mir dazu jemand einen guten Umsetzungsvorschlag machen?
Mein eigener Lösungsansatz wäre: Zwei bestimmte Bits definieren, wenn diese Bits dann empfangen werden, wird je nach Fall anders verarbeitet. Allerdings stoße ich dann irgendwann an meine maximal 255 verfügbaren Möglichkeiten und müsste schon 2 Bits senden.

Was Du brauchst ist eine Art Protokoll, das den Inhalt und die Bedeutung der übertragenen Daten definiert. Wie das aussieht, hängt von den Daten selbst ab.
Bei Daten mit dynamischer Länge kann man z.B. zuerst eine Kennung, dann die Länge und dann die Nutzdaten übertragen.
Kommt es auf Geschwindigkeit an, versucht man möglichst viele Informationen in wenigen Bits zu übertragen.
Apropos Bits. Du sprichst in Deiner Frage von Bits, dann aber von 255 Möglichkeiten? Das passt nicht ganz zusammen. Mit einem Bit hast Du genau 2 verschiedene Zustände, mit 2 Bits sind es 4. Nur bei einem Byte (8 Bit) hast Du 256 verschiedene Zustände (0 bis 255).

Hallo.

Danke für Eure Antworten. Dann werde ich es mal so umsetzten :wink:

PS: Ich weiß eigentlich was ein Bit und ein Byte sind :smiley: Habe da wohl schneller geschrieben als Nachgedacht :wink: