zwei arduinos miteinander sprechen lassen

Hallo zusammen,

ich versuche gerade zwei Arduinos miteinander Sprechen zu lassen. Das ganze will ich über i2c laufen lassen. Hierfür soll der Master einen String übermitteln, der Anweisungen welche mittels "," getrennt sind übersendet.

das ganze läuft über den Eventhandler soweit ist mir das klar, über Wire.available erhalte ich aussage darüber ob noch gesendet wird, aber wie bekomme ich die Trennung hin? ich lese ja byte für byte ein in den Beispielen von der Software. Oder muß ich erst alles in einen String packen und diesen dann danach erst zerteile? Wenn ja kann ich alles auf einmal auslesen irgendwie?

Danke schon mal

Steve

Wire.write() kann mehrere Sachen verarbeiten:

  • C-Strings, d.h. Null-terminierte char Arrays
  • char Arrays mit Längen-Angabe
  • Byte-Arrays mit Längen-Angabe
  • einzelne Bytes

C-Strings sind hier die beste Wahl, wenn du mit Trenn-Zeichen arbeiten willst. Du brauchst außerdem ein End-Zeichen damit du weißt wann du fertig bist.

Man kann das Byte für Byte einlesen und on-the-fly wieder in Zahlen wandeln, aber vielseitiger ist wenn man erst mal einen ganzen String einliest und danach zerteilt. Dazu braucht man mehr Speicher, aber man ist auch wesentlich flexibler.

Denke auch daran, dass du Integer vor dem Senden in einen C-String wandeln musst! (mit itoa()). Wenn du einfach einen Integer an Wire.write() übergibst wird das Binär gesendet. Dann kommt Müll heraus wenn du auf der anderen Seite einen String erwartest.

Super vielen dank,

das hat mir schon mal geholfen funktioniert jetzt fast. Auch der Hinweis mit itoa hat mir einiges an Problemen erspart.

Jetzt habe ich aber ein neues Problem. Ich lese mit einem Ethernetshield die Uhrzeit von einem Time-Server. Nun würde ich gerne die Stunden und Minute über die i2c schicken. Nun ist aber die Zeit dummerweise ein unsigned long... Wie bekomme ich das den geändert um es zu übertragen?

Steve

Schau dir mal das an:
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html

Da siehst du wenn du runter scrollst diese Funktion:

char* ultoa (unsigned long val, char *s, int radix)

unsigned long to array

Funktioniert genauso wie itoa() aber mit unsigned long statt statt int. Du kannst das auch statt itoa() nehmen. Dann sparst du etwas Flash.

Nun würde ich gerne die Stunden und Minute über die i2c schicken. Nun ist aber die Zeit dummerweise ein unsigned long...

Üblicherweise senden RTC-Module auf I2C die Zeit gern BCD codiert. (ca. 6 Byte)
Wenn du aber c-strings vorziehst und wirklich nur "hh:mm" willst, ist eine unsigned long unixtime als Text nicht unbedingt das einfachste.

Hallo,

ich hätte dazu noch eine Frage.
Ich beim senden von strings die Länge begrenzt?
Ich würde gerne Datum und Zeit in einem Strung zum Slave schicken.

Hier ein Beispiel:

16.2.2015;13:20:37;

Strings in C sind NULL-terminiert:

Wenn du sowas machst:

char str[] = "test"

macht das der Compiler für dich und erzeugt ein Array der Länge 5.

Auch Konviertierungs-Funktionen wit itoa() terminieren automatisch die Strings.

Aber wenn du z.B. Zeichen per Hand in einer for-Schleife in ein char Array schreibst bist du dafür selbst verantwortlich.

carnefix:
Ich beim senden von strings die Länge begrenzt?

Der I2C Buffer hat 32 Byte.
Damit ist die maximale Länge festgelegt.

Serenifly:
Strings in C sind NULL-terminiert:

Rheinwerk Computing :: C von A bis Z – 11.11 Strings/Zeichenketten (»char«-Array)

Wenn du sowas machst:

char str[] = "test"

macht das der Compiler für dich und erzeugt ein Array der Länge 5.

Auch Konviertierungs-Funktionen wit itoa() terminieren automatisch die Strings.

Aber wenn du z.B. Zeichen per Hand in einer for-Schleife in ein char Array schreibst bist du dafür selbst verantwortlich.

Danke für die Antwort.
Bei mir muss der Master die aktuelle Zeit und das Datum erfassen und es dann zum Slave schicken.
Die String ist 1:1 wie oben und hat immer die gleiche Länge.

Wird beim übertragen noch irgendein Zeichen hinzugefügt oder reicht es wenn ich einfach beim Slave ein String mit 20+1 (für '\0') Stellen erwarte?

carnefix:
oder reicht es wenn ich einfach beim Slave ein String mit 20+1 (für '\0') Stellen erwarte?

Sollte ausreichen

Und wie oben gesagt sind die Puffer bei I2C kleiner als bei Serial. Also sollte man da keine Romane senden und es bei längeren Sachen lieber Binär übertragen. Aber 21 Byte geht noch.

Passt dies dann ungefähr?

Master:

// Init

DS1307 rtc; // Real-Time-Clock
char datetime[21] = "";

Wire.begin();

void loop() {

rtc.get(&second, &minute, &hour, &day, &month, &year);
datetime = String(day) + "." + String(month) + "." + String(year) + ";";
datetime += String(hour) + ":" + String(minute) + ":" + String(second) + ";";

// start measurement on master
// save data with date and time
// show data, date and time on display

// sending the date and time is the trigger to start the measuring on the slave
Wire.beginTransmission(9); // transmit to device #9
Wire.write(datetime);        // sends date and time (dd.mm.yyyy;hh:mm:ss;)
delay(200);
Wire.endTransmission();    // stop transmitting

}

Slave:

// Init
Wire.begin(9);
char datetime[21] = "";
char prev_datetime = datetime;
Wire.onReceive(receiveEvent); // register event // get x

void receiveEvent(int howMany) {
  char datetime = Wire.read();
}

void loop() {

  // wait for trigger from master

  // if the previous date and time has changed, start the new measurement

  if (strcmp(prev_datetime, datetime) != 0){ 
    // start measurement
    // save data with date and time
    // show all on display

    prev_datetime = datetime;
  }
  
  
}

Passt dies dann ungefähr?

nein, denn

char datetime[21]; // ist kein string-Objekt

rtc.get(&second, &minute, &hour, &day, &month, &year);
sprintf(datetime, "%02d.%02d.%04d %02d:%02d:%02d",
                  day, month, year, hour, minute, second );  // sprintf ist nicht optimal, aber einfach

lcd.write(datetime);  // show datetime on display
void receiveEvent(int howMany) {
  char datetime = Wire.read();
}

Das ist völliger Unsinn, Slave Receive solltest du dir nochmal ansehen :wink:

Hallo michael_x,

danke für die schnelle Antwort.

Die Variable datetime heißt eigentlich ausgabe, was ich aber hier umgeändert hatte zur Veranschaulichung.
Nach dem erfassen der zeit wird es dann als datetime gespeichert und die ausgabe wird weiter mit messdaten aufgefüllt.

Ich habe bereits ein voll Funktionsfähiges Programm zur Messung und Darstellung auf dem Touch Display bekommen. Ich muss es nur mit dem I2C-bus für mehrere Boards erweitern.

Ich habe ein Teil des Codes beim Master übersehen, es wird eigentlich so deklariert:

String datetime[21] = "";

Ist dies für den Slave korrekt?

void receiveEvent(int howMany)
{
  if (Wire.available() == 21) // Wire.available() = number of bytes to read
  {
    for(int i=0; i<20 ; i++) // till 20 since '\0' was also sent
    {
      datetime[i] = Wire.read ();
    } 

  }

}

Gruß carnefix

carnefix:
Ich habe ein Teil des Codes beim Master übersehen, es wird eigentlich so deklariert:

String datetime[21] = "";

Das sind 21 String Objekte. Ohne die String Objekt / char string[] Diskussion aufzuwärmen, ist das aber auch nicht das was du willst.

Hallo Michael_x,

danke für die Info.
Das wurde von meinem Vorgänger erstellt. Es ist hiermit behoben worden.

War der Slave Reciever so in Ordnung?
Der ursprüngliche Code ist sehr lang und umfangreich und muss deshalb an vielen Stellen ergänzt werden.

Ich wollte es im vorraus wissen, bevor ich es implementiere.

Gruß Carnefix

War der Slave Reciever so in Ordnung?
Der ursprüngliche Code ist sehr lang und umfangreich und muss deshalb an vielen Stellen ergänzt werden.

Wenn du die Zeit als Text, und ein zusätzliches '\0' sendest, und insgeamt so viele Zeichen sendest, ist das schonmal besser als der vorige Ansatz.
Warum verwendest du nicht den Parameter int numbytes ?
Und fügst das Text-Ende selbst dazu ?

Ausserdem empfiehlt es sich immer, Teile bei denen man nicht sicher ist ob es funktioniert, gleich zu testen, besonders wenn man Angst hat, viel Fleißarbeit umsonst zu machen, oder wenn man schnell erste Erfolgserlebnisse sehen will.

Hallo,

der Code den ich bekommen hatte, ist voll funktionsfähig und macht keine Probleme.
Deshalb habe ich mich nicht komplett mit dem vorherigen code befasst.

Ich werde mal schauen was mit den aktuellen Code rauskommt.

Vielen Dank an alle die mir geholfen haben.

Gruß Carnefix

Hallo,

ich hätte noch eine Frage.

Ich möchte nun, dass der Slave in void setup() auf ein Signal vom Master wartet und der Master ebenso auf eine Antwort vom Slave wartet, bevor beide in die loop() gehen.

Beim Master kann ich es ja mit einem requestFrom, machen, so dass es auf ein Signal des Slaves wartet.

Wie kann ich das beim Slave realisieren?

Gruß Carnefix

Ich möchte nun, dass der Slave in void setup() auf ein Signal vom Master wartet und der Master ebenso auf eine Antwort vom Slave wartet, bevor beide in die loop() gehen.

Warum?

Hallo,

damit der Master wartet, bis die Initialisierung des Slaves abgeschlossen ist.
Ich möchte nicht, das der Master bereits änfangt Daten zu senden, wenn der Slave noch am starten ist.

Daruch soll der Slave kein Triggern des Masters verpassen damit ich am Ende die gleiche Menge an Messdaten gespeichert habe.