I2c mehrere Werte senden

Guten Abend,

ich sitze nun schon eine ganze Zeit an einem Problem, für das ich einfach keine Lösung finde(habe auch schon viele Seiten und Foren durchsucht), Ich möchte per I2C mehrere Daten von einem Raspberry PI an einen Arduino senden. Später möchte ich damit noch Motoren steuern aber erstmal Eins nach dem Anderen.
Wenn ich den Code ausführe, geben mir die beiden Serial.println Anweisungen immer den 2. Wert des Python-Codes und 255. Der erste Wert geht also verloren. Woran kann das liegen? Danke schon mal im vorraus.

Edit: Gibts hier sowas wie Code-tags?

Grüße Scio__

Master-Code(Raspberry):

from smbus import SMBus
addr = 0x8

Steps1 = 0
Steps2 = 0

while True:
print("Steps1: ")
Steps = input()
print("Steps2: ")
Steps2 = input()

bus = SMBus(1)
bus.write_byte(addr, int(Steps))
bus.write_byte(addr, int(Steps2))
bus.close()

Slave-Code(Arduino):
#include <Wire.h>
#include <AccelStepper.h>
AccelStepper myStepper(1,7,6);

volatile char x=0;
char xalt = 0;
volatile char y =0;

void setup() {
// Join I2C bus as slave with address 8
Wire.begin(0x8);

// Call receiveEvent when data received
Wire.onReceive(receiveEvent);
myStepper.setMaxSpeed(1000);
Serial.begin(9600);
myStepper.setAcceleration(400);

}

// Function that executes whenever data is received from master
void receiveEvent(int howMany) {
while(Wire.available())
{
x = Wire.read();
y = Wire.read();
}

}
void loop() {
//Serial.print("x[0] =");
Serial.println(x);
// Serial.println("");
// Serial.print("x[1} =");
Serial.println(y);
//Serial.println("");

//if (x!=xalt){
// myStepper.move((int)x*100);}

//while(myStepper.distanceToGo() !=0)
//{
// myStepper.run();
//}

//xalt = x;
}

[code] und danach [/code] ohne *

Gruß Tommy

Neben @Tommy56 Hinweis noch die Anmerkung, das die Codetags in einer extra Zeile stehen müssen.
Du kannst auch in der Arduino-IDE über BEARBEITEN - FÜR FORUM KOPIEREN den gesamten Sketch mitnehmen und einfügen. Dann brauchst Dich um nicht's anderes kümmern.

Aber schon mal als Ansatz:
Wie sieht denn Dein Wertebereich aus?
Welchen Wert kann Steps / Steps2 annehmen?

Die variable Steps ist glaube ich erstmal ein String und wird dann erst in write_byte Anweisung auf Int gewandelt. Ich weiß, das man bei i2c nur 1 Byte auf einmal senden kann aber komischerweiße hat es, als ich das mit einer Variable gemacht habe, auch geklappt....

Wer sagt das? Schau Dir mal das Beispiel an.

Gruß Tommy

Ich habs mit mal angesehen. Sieht für mich recht ähnlich aus. Konnte meinen Fehler leider nicht entdecken...

Lies mal die Doku. write hat optional einen 2. Parameter, der dort auch verwendet wurde.

Gruß Tommy

Einen RasPi habe ich nicht, daher muß ich es Dir überlassen zu ergründen, wieviel Bytes bei bus.write_byte(addr, int(Steps)) verschickt werden.

Anstelle RasPi verwende ich einen ESP32 mit 32 Bit µC, da besteht ein Integer aus 4 Bytes.

Zum Verschicken packe ich zwei Integerzahlen in eine Struktur (struct), die wiederum Teil einer Einheit (union) ist, damit ich ein Feld vom Typ byte erhalte, dessen Elemente den selben Speicherbereich adressieren wie die Struktur. Das Feld vom Typ byte wird zur Übertragung der Daten verwendet.

Der Empfänger muß die identische Datenstruktur erwarten, die Variablennamen dürfen aber abweichen.

// ESP32 als I2C-MASTER

#include <Wire.h>

const byte SLAVEADDRESS = 8;
const byte analogPin = 34;

const byte ANZAHL = 2 * sizeof(int);
union {
  byte bytes[ANZAHL];
  struct {
    int analogWert;
    int zufallsWert;
  };
} tx;

void loop()
{
  uint32_t jetzt = millis();
  static uint32_t vorhin = jetzt;
  if (jetzt - vorhin >= 1000)
  {
    vorhin = jetzt;
    tx.analogWert = map(analogRead(analogPin), 0, 4095, -1023, 1023);
    tx.zufallsWert = random(0, 1000);
    Wire.beginTransmission(SLAVEADDRESS);

    for (byte j = 0; j < ANZAHL; j++)
    {
      Wire.write(tx.bytes[j]);
      Serial.print(tx.bytes[j], HEX); Serial.print(' ');
    }
    Wire.endTransmission();
    Serial.print(F("\ttx.analogWert: ")); Serial.print(tx.analogWert); Serial.print(F("\ttx.zufallsWert: ")); Serial.println(tx.zufallsWert);
  }
}

void setup()
{
  Wire.begin();
  Serial.begin(115200);
  delay(1000);
  Serial.println(F("\nStart"));
  Serial.print(F("Anzahl Byte für int: ")); Serial.println(sizeof(int));
}
// Mega2560 als I2C-SLAVE

#include <Wire.h>
const byte SLAVEADDRESS = 8;
const byte ANZAHL = 8;
union {
  byte bytes[ANZAHL];
  struct {
    int32_t analogWert;
    int32_t zufallsWert;
  };
} rx;

volatile int32_t rxAnalog = 0;
volatile int32_t rxZufall = 0;
bool neu = false;

void setup()
{
  Wire.begin(SLAVEADDRESS);
  Wire.setClock(400000L); //setzt die I²C Taktfrequenz auf 400kHz
  Wire.onReceive(receiveEvent);
  Serial.begin(9600);
  Serial.println(F("\nStart"));
}

void receiveEvent(int numBytes)
{
  if (ANZAHL == numBytes)
  {
    for (byte j = 0; j < numBytes; j++)
    {
      rx.bytes[j] = Wire.read();
    }
    rxAnalog = rx.analogWert;
    rxZufall = rx.zufallsWert;
    neu = true;
  }
}

void loop()
{
  if (neu)
  {
    neu = false;
    for (byte j = 0; j < ANZAHL; j++)
    {
      Serial.print(rx.bytes[j], HEX); Serial.print(' ');
    }
    Serial.print(F("\tanalog: ")); Serial.print(rxAnalog); Serial.print(F("\tzufall: ")); Serial.println(rxZufall);
  }
}

Serieller Monitor Sender:

14:24:15.661 -> Start
14:24:15.661 -> Anzahl Byte für int: 4
14:24:16.643 -> 1F 0 0 0 75 3 0 0 tx.analogWert: 31 tx.zufallsWert: 885
14:24:17.646 -> 1F 0 0 0 49 3 0 0 tx.analogWert: 31 tx.zufallsWert: 841
14:24:18.649 -> 1F 0 0 0 5D 1 0 0 tx.analogWert: 31 tx.zufallsWert: 349
14:24:19.652 -> 1E 0 0 0 70 0 0 0 tx.analogWert: 30 tx.zufallsWert: 112
14:24:20.655 -> 1E 0 0 0 B1 0 0 0 tx.analogWert: 30 tx.zufallsWert: 177
14:24:21.658 -> 1E 0 0 0 16 0 0 0 tx.analogWert: 30 tx.zufallsWert: 22

Serieller Monitor Empfänger:

14:24:15.460 -> Start
14:24:16.643 -> 1F 0 0 0 75 3 0 0 analog: 31 zufall: 885
14:24:17.646 -> 1F 0 0 0 49 3 0 0 analog: 31 zufall: 841
14:24:18.649 -> 1F 0 0 0 5D 1 0 0 analog: 31 zufall: 349
14:24:19.652 -> 1E 0 0 0 70 0 0 0 analog: 30 zufall: 112
14:24:20.655 -> 1E 0 0 0 B1 0 0 0 analog: 30 zufall: 177
14:24:21.658 -> 1E 0 0 0 16 0 0 0 analog: 30 zufall: 22

Evtl. sollte man die int32_t-Schreibweise auf beiden Seiten benutzen. (Übersichtlichkeit)
Bei 2 int ist #pragma pack nicht notwendig, sonst könnte es notwendig werden.

Gruß Tommy

Danke, für die Antworten, ich könnte es lösen, indem ich die beiden Variablen durch ein Array ersetzt habe und dann hochzähle. Allerdings funktioniert das nur, wenn ich die Zählvariabls im mainloop zurücksetze, nicht im Interrupt. Gibt's da einen Grund dafür? Ich überlege sowieso gerade das Projekt etwas umzustrukturieren und Arduinonals Master zu nehmen. Vielleicht wird dadurch Einiges einfacher. Danke nochmal für die Hilfe :slight_smile:

Grüße
Scio

Du sprichst in Rätseln.

Kannst Du das mal ein wenig erläutern?
ggfls. auch mit kommentiertem Code?

Wie oben gezeigt kann man sowas schön mit Unions lösen. Damit kann man Variablen sowohl einzeln als auch als Array ansprechen weil sie die gleichen Speicher belegen. Dadurch bleiben die sprechenden Namen erhalten

Also, ich habe jetzt einiges an meinem Projekt geändert: Kein Raspberry mehr, sondern ein ESP32. Eigentlich sollte dieser als Slave auftreten, da es auf dem ESP die onRequest function nicht gibt (oder irgendwas was damit zusammen hängt) habe ich es versucht anders zu lösen. Ich habe zusätzlich zu den i2c Kabeln noch eine weitere Leitung gezogen, die auf seitens des ESP's(Master) an ein Interrupt gebunden ist. Dieses wird auch ausgelöst, allerdings kommen die Daten nicht an. Woran kann das liegen? Mir ist vollkomme klar, dass man das eigentlich nicht so machen sollte, allerdings ist mir keine bessere Lösung eingefallen. Es ist zwingend erforderlich, dass der Master nur senden darf, wenn der Slave auch bereit dafür ist. Die Delays in der Loopfunktion des Slaves sollen die "Nicht-Bereitschaft" des Slaves simulieren.

Master:

#include <Wire.h>
volatile byte x = 0;

void IRAM_ATTR isr() {
  
    Wire.beginTransmission(4); // transmit to device #4
    Wire.write(x);
    Wire.endTransmission();    // stop transmitting
    x++;
   // Serial.println("Interrupt!"); 
    
}

void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
  pinMode(15, INPUT_PULLUP);
  attachInterrupt(15,isr,HIGH); 
  pinMode(2,OUTPUT); 
  Serial.begin(9600); 
}



void loop()
{
 
}

Salve:

#include <Wire.h>

volatile int x = 0; 
void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(115200);           // start serial for output
  pinMode(7, OUTPUT);
  pinMode(7, LOW);

}

void loop()
{
  pinMode(7, HIGH);
  delay(1000); 
  pinMode(7,LOW); 
  delay(5000); 
  Serial.println(x, DEC); 
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
 x = Wire.read();
 }

Wer sagt das?

Gruß Tommy

Edit: Gegenbeweis

Das wird nichts.
gehe davon aus, das in einer isr alles tot ist, ausser das, wa Du da machst.
Du kannst addieren/subtrahieren Auch dividieren und multiplizieren.
Du kannst toggeln.
Viel mehr Nicht!
Schon gar nicht Wire!
Dann editier ich meinen mal auch noch...

alt:

volatile byte x = 0;

void IRAM_ATTR isr() {

  Wire.beginTransmission(4); // transmit to device #4
  Wire.write(x);
  Wire.endTransmission();    // stop transmitting
  x++;
  // Serial.println("Interrupt!");

}

neu:

volatile byte x = 0;
byte lastbyte y = x;

void IRAM_ATTR isr() {
  x++;
}

im loop neu einfügen

sendTicker();

Und eine neue Funktion schreiben

void sendTicker()
{
  if (y != x)
  {
    Wire.beginTransmission(4); // transmit to device #4
    Wire.write(x);
    Wire.endTransmission();    // stop transmitting
    y = x;
  }
}

Es fehlt digitalPinToInterrupt().

So geht es:

// ESP32 als Master
#include <Wire.h>
volatile byte x = 0;
bool merker = false;

void IRAM_ATTR isr() {
  merker = true;
  x++;
}

void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
  pinMode(15, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(15), isr, FALLING);
}



void loop()
{
  if (merker) {
    merker = false;
    Wire.beginTransmission(4); // transmit to device #4
    Wire.write(x);
    Wire.endTransmission();    // stop transmitting
  }
}
// Mega2560 als SLAVE

#include <Wire.h>

volatile byte x = 0, y=0;
void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(115200);           // start serial for output
  pinMode(7, OUTPUT);
  pinMode(7, LOW);

}

void loop()
{
  pinMode(7, HIGH);
  delay(1000);
  pinMode(7, LOW);
  delay(5000);
  Serial.print(x, DEC);  Serial.print('\t');  Serial.println(y, DEC);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  x = Wire.read();
  y++;
}

Nur der Zweite :roll_eyes: