Datenübertragung von Serial print zu I2C wechseln

Hallo

Ich habe vor einiger Zeit das Senden von Zahlenwerten über I2C erfolgreich machen können. Nun habe ich von meinem ursprünglich funktionierenden Sketch der per Serial sendet, zu I2C wechseln wollen, da ich den Datenaustausch zwischen den Arduinos per I2C erledigen kann und somit die serielle UART Schnittstelle für anderes frei halten kann.
Nun hier den seriellen Sende- und Empfangsteil der funktioniert hat:

Empfangs- Sende Arduino seriell:

while(Serial.available() > 0){
    var = Serial.read();
    switch(var){                                        
      case 'A' : DP1 = Serial.parseInt();
                 break;
      case 'B' : DP2 = Serial.parseInt();
                 break;
      case 'C' : DP3 = Serial.parseInt();
                 break;
      case 'D' : DP4 = Serial.parseInt();
                 break;
      case 'E' : DP5 = Serial.parseInt();
                 break;
      case 'F' : DP6 = Serial.parseInt();
                 break;
      case 'G' : Serial2.print('G');
                 Serial2.println(Serial.parseInt());
                 Serial2.flush();
                 break;
      case 'H' : Serial2.print('H');
                 Serial2.println(Serial.parseInt());
                 Serial2.flush();
                 break;
      case 'I' : Serial2.print('I');
                 Serial2.println(Serial.parseInt());
                 Serial2.flush();
                 break;
      case 'J' : Serial2.print('J');
                 Serial2.println(Serial.parseInt());
                 Serial2.flush();
                 break;
      case 'K' : Serial2.print('K');
                 Serial2.println(Serial.parseInt());
                 Serial2.flush();
                 break;
      case 'L' : Serial2.print('L');
                 Serial2.println(Serial.parseInt());
                 Serial2.flush();
                 break;
      case 'M' : HTLR = Serial.parseInt();
                 break;

Und hier der Testsketch per I2C der funktioniert hat.

Sende Arduino I2C:

#include <Wire.h>

int x = 0;
int y = 20;
int z = 300;

void setup() {
  // Start the I2C Bus as Master
  Wire.begin();
  Wire.setClock(33000L);
  
}

void loop() {

  x = x + 1;
  
  Wire.beginTransmission(9);    // transmit to device #9
  Wire.write('G');              //sends character G to I2C
  Wire.write((uint8_t*)&x, sizeof x);        // sends x
  Wire.flush();
  delay(3);
  Wire.write('H');              //sends character H to I2C
  Wire.write((uint8_t*)&y, sizeof y);        // sends y
  Wire.flush();
  delay(3);
  Wire.write('I');              //sends character I to I2C
  Wire.write((uint8_t*)&z, sizeof z);        // sends z
  Wire.flush();
  Wire.endTransmission();       // stop transmitting
}

Empfangs Arduino I2C:

#include <Wire.h>

const unsigned long Baudrate = 38400; //starts serial port for the Bluetooth module with a baudrate by 38200 b/s

char var = 0;
int DP1 = 0;
int DP2 = 0;
int DP3 = 0;

void setup() {
  
  // Start the I2C Bus as Slave on address 9
  Wire.begin(9);
  // Attach a function to trigger when something is received.
  Wire.onReceive(receiveEvent);

  Serial.begin(Baudrate);
}

void loop() {

Serial.print("DP1 = ");
Serial.println(DP1);
Serial.print("DP2 = ");
Serial.println(DP2);
Serial.print("DP3 = ");
Serial.println(DP3);

}

void receiveEvent(int bytes) {
  while(Wire.available() > 0){
  var = Wire.read();    // read one character from the I2C
  switch(var){
    case 'G' : DP1 = Wire.read();               //read the first part of DP1
               DP1 |= Wire.read() << 8;  //shift it 8 bits to the left and add the second part to DP1
               break;
    case 'H' : DP2 = Wire.read();
               DP2 |= Wire.read() << 8;
               break;
    case 'I' : DP3 = Wire.read();
               DP3 |= Wire.read() << 8;
               break;
  }
  }
}

Wenn ich nun den seriellen Sketch mit dem erfolgreich getesteten I2C Sketch kombiniere, sendet er nicht alle Werte:

Sende Arduino I2C:

  while(Serial1.available() > 0){
    var = Serial1.read();
    switch(var){                                        
      case 'A' : DP1 = Serial1.parseInt();
                 break;
      case 'B' : DP2 = Serial1.parseInt();
                 break;
      case 'C' : DP3 = Serial1.parseInt();
                 break;
      case 'D' : DP4 = Serial1.parseInt();
                 break;
      case 'E' : DP5 = Serial1.parseInt();
                 break;
      case 'F' : DP6 = Serial1.parseInt();
                 break;
      case 'G' : DP7 = Serial1.parseInt();
                 Wire.beginTransmission(2);
                 Wire.write('G');
                 Wire.write((uint8_t*)&DP7, sizeof DP7);
                 Wire.flush();
                 Wire.endTransmission();
                 break;
      case 'H' : DP8 = Serial1.parseInt();
                 Wire.beginTransmission(2);
                 Wire.write('H');
                 Wire.write((uint8_t*)&DP8, sizeof DP8);
                 Wire.flush();
                 Wire.endTransmission();
                 break;
      case 'I' : DP9 = Serial1.parseInt();
                 Wire.beginTransmission(2);
                 Wire.write('I');
                 Wire.write((uint8_t*)&DP9, sizeof DP9);
                 Wire.flush();
                 Wire.endTransmission();
                 break;
      case 'J' : DP10 = Serial1.parseInt();
                 Wire.beginTransmission(2);
                 Wire.write('J');
                 Wire.write((uint8_t*)&DP10, sizeof DP10);
                 Wire.flush();
                 Wire.endTransmission();
                 break;
      case 'K' : HFLR = Serial1.parseInt();
                 Wire.beginTransmission(2);
                 Wire.write('K');
                 Wire.write((uint8_t*)&HFLR, sizeof HFLR);
                 Wire.flush();
                 Wire.endTransmission();
                 break;
      case 'L' : HFUD = Serial1.parseInt();
                 Wire.beginTransmission(2);
                 Wire.write('L');
                 Wire.write((uint8_t*)&HFUD, sizeof HFUD);
                 Wire.flush();
                 Wire.endTransmission();
                 break;
      case 'M' : HTLR = Serial1.parseInt();
                 break;

Empfangs Arduino I2C:

void receiveEvent(int bytes) {
while(Wire.available() > 0){
    var = Wire.read();
    switch(var){                                        
      case 'G' : DP7 = Wire.read();
                 DP7 |= Wire.read() << 8;
                 break;
      case 'H' : DP8 = Wire.read();
                 DP8 |= Wire.read() << 8;
                 break;
      case 'I' : DP9 = Wire.read();
                 DP9 |= Wire.read() << 8;
                 break;
      case 'J' : DP10 = Wire.read();
                 DP10 |= Wire.read() << 8;
                 break;
      case 'K' : HFLR = Wire.read();
                 HFLR |= Wire.read() << 8;
                 break;
      case 'L' : HFUD = Wire.read();
                 HFUD |= Wire.read() << 8;
                 break;

Bei mehreren Werten die zum gleichen Arduino gehen, sende ich dann alle hintereinander wie im Testsketch I2C versucht:

Wire.beginTransmission(2);
        Wire.write('t');
        Wire.write((uint8_t*)&BUT16, sizeof BUT16);
        Wire.flush();
        delay(3);
        Wire.write('G');
        Wire.write((uint8_t*)&DPangleMAX, sizeof DPangleMAX);
        Wire.flush();
        delay(3);
        Wire.write('H');
        Wire.write((uint8_t*)&DPangleMAX, sizeof DPangleMAX);
        Wire.flush();
        delay(3);
        Wire.write('I');
        Wire.write((uint8_t*)&DPangleMAX, sizeof DPangleMAX);
        Wire.flush();
        delay(3);
        Wire.write('J');
        Wire.write((uint8_t*)&DPangleMAX, sizeof DPangleMAX);
        Wire.flush();
        Wire.endTransmission();

Nun scheint es per I2C nicht so zu funktionieren wie beim Testsketch I2C und sendet/empfängt nicht alles. Es werden auch nicht immer die gleichen Buchstaben/Zahlen gesendet, da dies Joysticks sind die Werte ausgeben. Kann mir hier jemand helfen, wie ich die Daten wie bei Serial korrekt senden kann, sodass es auch per I2C funktioniert?
Zur Info noch: Am Serial1 Port vom Mega hängt ein Xbee welches die Joystick Werte empfängt. Ein Teil der Daten wird vom Mega weiterverarbeitet, andere werden dann zum nächsten Arduino gesendet.

Grüsse

Stef

Verwenden Sie kein delay() oder flush() für Ihre I2C-Transaktion

  Wire.beginTransmission(2);
  Wire.write('t');
  Wire.write((uint8_t*)&BUT16, sizeof BUT16);
  Wire.write('G');
  Wire.write((uint8_t*)&DPangleMAX, sizeof DPangleMAX);
  Wire.write('H');
  Wire.write((uint8_t*)&DPangleMAX, sizeof DPangleMAX);
  Wire.write('I');
  Wire.write((uint8_t*)&DPangleMAX, sizeof DPangleMAX);
  Wire.write('J');
  Wire.write((uint8_t*)&DPangleMAX, sizeof DPangleMAX);
  Wire.endTransmission();
2 Likes

Das delay() ist erforderlich, weil ich sonst nicht alle Daten erhalte! Dann erhalte ich nur immer jeden 5. Wert z.B. oder so. Den Takt musste ich auf 33kHz stellen mit einem delay(3); dann bekomme ich alles.
Ohne flush, ok. Das habe ich vom Serial übernommen, da es dort notwendig war um zu warten, bis die Daten komplett gesendet wurden.

Ist das ein Scherz? : :man_shrugging: Stehen die Werte nicht in den Variablen?

Wire.beginTransmission() speichert alle Schreibvorgänge () in einem Buffer und der Buffer wird gesendet, wenn Sie die Wire.endTransmission() ausgeben.

1 Like

Nein, ist kein Scherz! Wir haben dies hier getestet: Servo zucken mit SoftwareSerial und Servo Lib - #63 by stef308

Da habe ich immer jeden dritten Wert mit 33kHz erhalten und mit den Standard 100kHz noch weniger!

Können Sie mich daran erinnern, von und zu welchem Arduino Sie Daten senden?

Von einem Mega zu einem Uno werden die Daten gesendet, Mega als Master, Uno als Slave.

Und wie lang ist dieses Kabel zwischen den Controllern ?
Du hättest ja auch SoftwareSerial verwenden können.

Wenn Sie fünf Dinge falsch machen, funktioniert es möglicherweise besser, wenn Sie auch noch eine sechste Sache falsch machen. Sie sollten jedoch alle Probleme beheben.

Entfernen Sie das delay() und das flush() und senden Sie nicht zu viele serielle Daten oder zu viele I2C-Daten in der loop().

Möglicherweise ist ein einzelnes delay() am Ende der loop() erforderlich, oder Sie verwenden einen millis-timer.

Das geht aufgrund der verwendeten Servo Lib nicht weil dies zu Konflikten führt.
Die Kabellänge ist ca. 1.50m. Gewisse Daten kommen an, andere nicht. Den Testsketch I2C habe ich mit ca. 2m Kabel versucht und hat funktioniert.

Dein Kabel ist für I2C def. Zu lang.
I2C ist, wie der Name schon sagt, für die Geräte interne Nutzung entwickelt worden. Wenn es mit einem Testsketch funktioniert hat, ist es reiner Zufall und wird nicht von den Spezifikationen abgedeckt. Bei der Kabellänge solltest du min. abgeschirmte Kabel verwenden und evtl. noch Bus-Extender. Eine stabile Funktion wirst du anders nicht hin bekommen.
Und du musst auch immer vorhandene Störungen eingerechnet.

Ich habe das ganze in diesem System getestet, aber von einem Uno zu einem Uno mit 4.7kOhm zu 5V. Macht das einen Unterschied wenn ich nun von einem Mega zu einem Uno mit den 4.7kOhm zu 5V gehe? Habe gelesen, dass der Mega im Gegensatz zu einem Uno einen internen Widerstand für I2C von 10kOhm verbaut hat? Hat das vielleicht einen Einfluss?

Also habe ich es mit drei einfachen Drähten versucht

MEGA <---> UNO
 GND <---> GND
  20 <---> A4 (SDA)
  21 <---> A5 (SCL)

kein 4.7kOhm pull-up, kein nix.

MEGA-MASTERCODE:

#include <Wire.h>

int x = 0;
int y = 20;
int z = 300;

void setup() {
  Wire.begin(); // MEGA = MASTER
  Serial.begin(115200);
  Serial.println(F("\nGeben Sie ein beliebiges Zeichen ein, um zu beginnen"));
  while (Serial.available() == 0) yield();
}

void loop() {
  Wire.beginTransmission(9);
  Wire.write('X');
  Wire.write((uint8_t*)&x, sizeof x);
  Wire.write('Y');
  Wire.write((uint8_t*)&y, sizeof y);
  Wire.write('Z');
  Wire.write((uint8_t*)&z, sizeof z);
  Wire.endTransmission();

  x++;
  y++;
  z++;
  delay(1000);
}

UNO-SLAVE-CODE:

#include <Wire.h>

void i2cMessage(int numBytes) {
  int x = -1, y = -1, z = -1;

  if (numBytes == 9) {
    while (Wire.available() > 0) {
      switch (Wire.read()) {
        case 'X' :
          x = Wire.read();               //read the first part of DP1
          x |= Wire.read() << 8;  //shift it 8 bits to the left and add the second part to DP1
          break;
        case 'Y' :
          y = Wire.read();
          y |= Wire.read() << 8;
          break;
        case 'Z' :
          z = Wire.read();
          z |= Wire.read() << 8;
          break;
      }
    }
    Serial.print(F("I2C-Transaktion: "));
    Serial.print(F("\tx=")); Serial.print(x);
    Serial.print(F("\ty=")); Serial.print(y);
    Serial.print(F("\tz=")); Serial.println(z);
  } else Serial.println(F("falsche I2C-Transaktion"));
}

void setup() {
  Wire.begin(9); // UNO = SLAVE
  Wire.onReceive(i2cMessage);
  Serial.begin(115200);
  Serial.println(F("\nArduino UNO (Slave) bereit"));
}

void loop() {}

MEGA Serial Monitor

Geben Sie ein beliebiges Zeichen ein, um zu beginnen

  • Ich gebe ein Zeichen auf dem MEGA Serial Monitor ein, um den Code in Gang zu setzen

  • Der serielle Monitor (115200 Baud) für die UNO zeigt (Update mit 1Hz wie erwartet):

Arduino UNO (Slave) bereit
I2C-Transaktion: .x=0.y=20.z=300
I2C-Transaktion: .x=1.y=21.z=301
I2C-Transaktion: .x=2.y=22.z=302
I2C-Transaktion: .x=3.y=23.z=303
I2C-Transaktion: .x=4.y=24.z=304
I2C-Transaktion: .x=5.y=25.z=305
I2C-Transaktion: .x=6.y=26.z=306
I2C-Transaktion: .x=7.y=27.z=307
I2C-Transaktion: .x=8.y=28.z=308
I2C-Transaktion: .x=9.y=29.z=309
I2C-Transaktion: .x=10.y=30.z=310
I2C-Transaktion: .x=11.y=31.z=311
...

also scheint alles völlig in Ordnung zu sein, mit Standard-I2C-Geschwindigkeit.

vermisse ich etwas? (außer der Länge des Kabels...)

➜ Die Kabellänge ist ca. 1.50m

Ich denke das ist wirklich dein Problem

Es wird hier aber mit einem delay von 1 Sekunde gesendet!
Könnte dann der zusätzliche 4.7kOhm Widerstand beim Mega das Problem verursachen weil er schon 10kOhm hat?

Wenn Sie delay (1000); entfernen, funktioniert es immer noch perfekt. die daten werden auf der anderen seite sehr schnell gedruckt, aber in meinem test habe ich keine kommunikation verpasst.


Könnte dann der zusätzliche 4.7kOhm Widerstand beim Mega das Problem verursachen weil er schon 10kOhm hat?

Ja, es ist möglich. Der maximale Pullup-Widerstand basiert auf der benötigten Anstiegszeit des Clock (abhängig von der I2C-Taktfrequenz) und der Gesamtkapazität auf dem Bus


Sie könnten ein RS-485-Modul und die AltSoftSerial-Bibliothek verwenden. AltSoftSerial legt Pins für Rx/Tx fest, funktioniert aber mit der Servobibliothek

Wenn ich einen I2C extender verwenden möchte der die Reichweite erhöht, dann sollte es ja auch gehen.
Gibt es einen Extender den man vielleicht beim Mega anschliessen kann und am anderen Ende dann einfach den Uno? Gibt es da Schaltpläne die man auf einer eigenen Platine integrieren könnte?

Sie könnten an beiden Enden einen P82B715 verwenden. Wird nicht funktionieren, wenn nur auf der MEGA-Seite.

(Ihr I2C-Signal ist sehr wahrscheinlich empfindlich gegenüber elektromagnetischen Wellen von Motoren usw.)

Mit diesem Extender benötigst du nur einen.

1 Like

gute Wahl

Das sieht echt interessant aus! Vielen Dank für den Tipp :slight_smile:
Eine blöde Frage.... Schliesst man da einfach links und rechts an den Buchsen die I2C Kabel an? Für was sind denn die Pins unten noch zusätzlich? Als dritter Anschluss? Ich werde da aus dem Schema nicht schlau.