Integer von Arduino zu Arduino via I2C, speziell von I2C wieder auf Integer

Hallo,

ich habe eine dumme Frage und finde keine Antwort.
Ich möchte 4 Werte (2 würden reichen) von einem Arduino zu einem anderen Arduino über i2C übertragen, die Übertragung klappt auch schon ganz gut. Nur scheitere ich daran, die am Master empfangenen Werte wieder in Integer umzuwandeln.

Mein Slave misst und sendet in der Loop Schleife die als Integer definierten 1-3 stelligen Werte r,t,v,s (bitte Ignoriert die Seriellen befehle):

 //gekürzt für maximale Beitragsgröße im Forum




#include <Servo.h>
#include <OneWire.h>
#include <Wire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
Servo myservo;
Servo myservo2;
int potpin = 0;  // analog pin used to connect the potentiometer
int val; 

void setup() {
sensors.begin(); // put your setup code here, to run once:
myservo.attach(9);
myservo2.attach(10);
Wire.begin();
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(3), Pulse_Event, RISING);  // Enable interruption pin 2 when going from LOW to HIGH.
  pinMode (3, INPUT_PULLUP);
  delay(1000);
}

void loop() {
  // put your main code here, to run repeatedly:
 LastTimeCycleMeasure = LastTimeWeMeasured;  // Store the LastTimeWeMeasured in a variable.
  CurrentMicros = micros();  // Store the micros() in a variable.




  // CurrentMicros should always be higher than LastTimeWeMeasured, but in rare occasions that's not true.
  // I'm not sure why this happens, but my solution is to compare both and if CurrentMicros is lower than
  // LastTimeCycleMeasure I set it as the CurrentMicros.
  // The need of fixing this is that we later use this information to see if pulses stopped.
  if(CurrentMicros < LastTimeCycleMeasure)
  {
    LastTimeCycleMeasure = CurrentMicros;
  }





  // Calculate the frequency:
  FrequencyRaw = 10000000000 / PeriodAverage;  // Calculate the frequency using the period between pulses.


  

  
  // Detect if pulses stopped or frequency is too low, so we can show 0 Frequency:
  if(PeriodBetweenPulses > ZeroTimeout - ZeroDebouncingExtra || CurrentMicros - LastTimeCycleMeasure > ZeroTimeout - ZeroDebouncingExtra)
  {  // If the pulses are too far apart that we reached the timeout for zero:
    FrequencyRaw = 0;  // Set frequency as 0.
    ZeroDebouncingExtra = 2000;  // Change the threshold a little so it doesn't bounce.
  }
  else
  {
    ZeroDebouncingExtra = 0;  // Reset the threshold to the normal value so it doesn't bounce.
  }





  FrequencyReal = FrequencyRaw / 10000;  // Get frequency without decimals.
                                          // This is not used to calculate RPM but we remove the decimals just in case
                                          // you want to print it.





  // Calculate the RPM:
  RPM = FrequencyRaw / PulsesPerRevolution * 60;  // Frequency divided by amount of pulses per revolution multiply by
                                                  // 60 seconds to get minutes.
  RPM = RPM / 10000;  // Remove the decimals.





  // Smoothing RPM:
  total = total - readings[readIndex];  // Advance to the next position in the array.
  readings[readIndex] = RPM;  // Takes the value that we are going to smooth.
  total = total + readings[readIndex];  // Add the reading to the total.
  readIndex = readIndex + 1;  // Advance to the next position in the array.

  if (readIndex >= numReadings)  // If we're at the end of the array:
  {
    readIndex = 0;  // Reset array index.
  }
  
  // Calculate the average:
  average = total / numReadings;  // The average value it's the smoothed result.





  // Print information on the serial monitor:
  // Comment this section if you have a display and you don't need to monitor the values on the serial monitor.
  // This is because disabling this section would make the loop run faster.


int r = 164; // Lückenfüller

int s = map (RPM, 0, 1404, 0, 180);
sensors.requestTemperatures();
int t = map (sensors.getTempCByIndex(0), -55, 125, 0, 180);
int v = 136; // Lückenfüller
myservo.write (t); // Temperatur
myservo2.write (r); // Geschwindigkeit
delay(30); 
Wire.beginTransmission(2);

Serial.print("rpm.val=");
Wire.print ("r,");
      Serial.print(r);
      Wire.println(r);
      Serial.write(0xff);
      Serial.write(0xff);
      Serial.write(0xff);
 Serial.print("temp.val=");
 Wire.print("t,");
      Serial.print(t);
      Wire.println(t);
      Serial.write(0xff);
      Serial.write(0xff);
      Serial.write(0xff);
      
      Serial.print("volt.val=");
      Wire.print("v,");
      Serial.print(v);
      Wire.println(v);
      Serial.write(0xff);
      Serial.write(0xff);
      Serial.write(0xff);

      Serial.print("speed.val=");
      Wire.print("s,");
      Serial.print(r);
      Wire.println(160);
      Serial.write(0xff);
      Serial.write(0xff);
      Serial.write(0xff);
Wire.endTransmission();
     
}

am Master

#include <Wire.h>

void setup() {
Wire.begin(2);
Wire.onReceive(empfangen);
Serial.begin(9600);

}
void loop() {
  // put your main code here, to run repeatedly:
delay (100);

}

void empfangen(int anzahl)
{
  while(1<Wire.available())
  {
    char buchstabe = Wire.read();
    Serial.print(buchstabe);
  }
  char buchstabe = Wire.read();
  Serial.println(buchstabe);
}

bekomme ich dann auch folgendes im Seriellen Monitor ausgespuckt:

r,164
t,-72
v,136
s,160

r,164
t,-72
v,136
s,160

r,164
t,-72
v,136
s,160

Nun meine Frage:

wie bekomme ich am Master die Werte r,t,v,s wieder zum Integer gewandelt, zur Weiterverarbeitung im sketch? :confused:

Vermutlich mache ich es mir schon beim Senden zu leicht.

Funktionsbeschreibung:
Der Slave erfasst eine Temperatur mit einem DS18B20 Sensor und eine sehr tiefe Frequenz mit Hilfe der micros Funktion um eine Geschwindigkeit zu messen.
Beide Werte werden über i2C zum Master übertragen

Der Master erfasst eine mittlere Frequenz zur Drehzahlbestimmung , eine Spannung (Bordspannung), empfängt die Temperatur und die Geschwindigkeit über I2C und bereitet alles in einem Seriellen Protokoll für ein, an der Seriellen Schnittstelle angeschlossenes, NEXTION Display auf.

Hi

Wie war nun Dein Problem?
Aus dem Schnipsel (Der gerne in einen Code-Tag eingebettet werden will) erschließt sich mir Dein Problem nicht wirklich.
Auch sehen die Zahlen - wohl Terminal-Ausgaben - durchaus wir '3-stellige Integer' aus.

WAS schickst Du los und WAS kommt statt Dessen am anderen Ende an?

MfG

Setzen den Sketch bitte in Code-Tags, dazu Schaltfläche </> oben links im Editorfenster verwenden.
Dann wird dieser auch lesbar.
Das kannst du noch nachträglich machen.

void empfangen(int anzahl)
{
while(1<Wire.available())
{
char buchstabe = Wire.read();
Serial.print(buchstabe);
}
char buchstabe = Wire.read();
Serial.println(buchstabe);

Serielle Ausgaben in der ISR bringen dich zu fall.

Ungetestet:
(Aus meiner Wühlkiste, leicht für dich modifiziert)

#include <Streaming.h>
#include <Wire.h>

struct Datensatz
{
  int A;
  int B; 
};


Datensatz satz {4711,-122};

volatile bool frischeDatenDa = false;

void empfangen(int anzahl)
{ 
  if(anzahl==sizeof(satz)) 
  {
    uint8_t *ptr = (uint8_t *)&satz; 
    for(unsigned i = 0; i < sizeof(satz); i++ ) ptr[i] = uint8_t(Wire.read());
    frischeDatenDa = true;
  }
}


void setup() 
{
  Wire.begin(42);
  Wire.onReceive(empfangen);
  
  Serial.begin(9600);
  Serial << "Start: "<< __FILE__ << endl;
  
  Wire.beginTransmission(2);
  uint8_t *ptr = (uint8_t *)&satz; 
  for(unsigned i = 0; i < sizeof(satz); i++ ) Wire.write(ptr[i]);
  Wire.endTransmission();
}

void loop() 
{
  if(frischeDatenDa)
  {
    // tuwas mit satz
    frischeDatenDa = false;
  }

}
 while(1<Wire.available())

Das sieht mir so schon sehr seltsam aus.

Ob ein int 1 oder 3 stellig ist, hat ja nur mit der Darstellung bei Serial.print zu tun.

I2C - Übertragung macht man üblicherweise Binär , also mit write statt print und kennt die Größe und Struktur eines Datenblocks.

Dann braucht man auch nicht unbedingt solche Hinweistexte wie “r,” über I2C zu versenden.
(Oder hab ich da was falsch gesehen?)

Interessanter ist, dass die Werte

  • negativ sein können
  • großer als 127 sein können
  • kleiner als 32767 sind.

Dann passt jede Zahl in 2 byte und in 8 byte kann man alle 4 Variablen übertragen.

I2C sendet Byte-Pakete fester Länge. Andere Datentypen kann man per union auf Byte-Arrays abbilden.

Alternativ kann man auch wie in combies Beispiel den Datensatz auf einen (byte*) casten und so byteweise an Wire.write übergeben.

Danke für die Antworten,

ich habe noch große Schwierigkeiten im Verständniss der i2C Kommunikation.

Das "r," habe ich benutzt um zu sehen ob und was übertragen wird.
Deshalb hat auch nur Wire.print funktioniert um Buchstaben und Zahlen positiv wie negativ zu übertragen.

Jetzt gilt es für mich die werte aus dem Datenblock zu selektieren und irgendwie in die loop Schleife meines Sketches zur Weiterverarbeitung zu bekommen.

Jetzt gilt es für mich die werte aus dem Datenblock zu selektieren

Strukturen versenden, schmeckt dir also nicht......

ich habe noch große Schwierigkeiten im Verständniss der i2C Kommunikation.

Das stimmt!
z.B. hast du im ersten Beitrag konsequent Master und Slave vertauscht.

Strukturen versenden, schmeckt dir also nicht......

Doch das will ich, nur weiß ich im Moment noch nicht was ich tue bsw. wie ich es anstellen werde.

[Das stimmt!
z.B. hast du im ersten Beitrag konsequent Master und Slave vertauscht

Bestes Beispiel dafür, das ich noch nicht weiß was ich tue.

Ich habe hier im Forum einige Beispiele gefunden mit denen ich experimentieren werde.
Danach verstehe ich auch deinen geposteten Code und dann klappts bei mir auch mit dem I2C

Dh. für mich als Hausaufgabe:

*eine verwertbare Übertragung als byte über Wire.write zu Stande bringen und nicht als char wie bis jetzt.

*Danach widme ich mich dem ver- und endpacken mehrerer werte in einem 8 byte großen Datenblock

Ich habe hier im Forum einige Beispiele gefunden mit denen ich experimentieren werde.
Danach verstehe ich auch deinen geposteten Code und dann klappts bei mir auch mit dem I2C

Tipp:
Mache nicht so viel mit Beispielen...
Da besteht die Gefahr, das falsche zu lernen.

Versuche besser erst die Sprache zu lernen!
Und die heißt hier C++.
Dann verstehste die Beispiele auch sofort viel besser.

Dann verstehste die Beispiele auch sofort viel besser.

Also doch Beispiele angucken :smiling_imp: Natürlich nur parallel zur C++ Referenz.

Und experimentieren! Man kann gar nicht genug 15-Zeilen Sketche *) mit Serial.println zum Ausprobieren schreiben (und jeden zwanzigmal verändern). Meine Art des Lernens jedenfalls.

*) gern auch Dreizeiler genannt.

Das Erlernen von C++ habe ich als Ratschlag angenommen :slight_smile: .

Das stimmt schon mit dem Verständnis, alle meine Sketches sind zusammen kopiert aus Beispielen und Postings im Internet und irgendwie passend Kompiliert ;D

So wie auch meine gefundene Zwischenlösung die für künftige Projekte herhalten muss bis ich mehr Überblick über die Programmiersprache C++ und den Funktionen in Arduino habe.

Sie Funktioniert und ich habe sie für meine Bedürfnisse angepasst.

Diesmal mit sendendem Slave und empfangendem Master

Sender:

//Sender
#include <Wire.h>

int temp=0;
int spd=0;
unsigned char I2Cdata[4];

void setup()
{
  Serial.begin(9600);
    
 Wire.begin(2);  // dann mal los ;)
 
 Wire.onRequest(RequestData);
}

void loop()
{
 int z; // "alibi" variable
 z =analogRead(0);
 spd = map (z , 0,1023, 0, 255);
int v = 136;
int r = 160;


int  t = -23; // gemessene Temperatur in Grad 
 temp= (t + 55); // übertragener positiver Wert,  klingt blöd aber ich kann eh nur Temperaturen von -55 bis 125 = 180 grad messen
 
 I2Cdata[0] = temp;
 I2Cdata[1] = spd;
 I2Cdata[2] = v;
 I2Cdata[3] = r;
 
 
Serial.print("Temperatur = ");
 Serial.println(t);
 Serial.print("Geschwindigkeit= ");
 Serial.println(spd);
 Serial.print("Spannung = ");
 Serial.println(v);
 Serial.print("Drehzahl= ");
 Serial.println( r * 78);
 delay(50);  
}

void RequestData()
{
Wire.write(I2Cdata,4);
}

und der Empfänger:

// Empfänger
#include <Wire.h>

int temp=0;
int spd=0;
int v=0;
int r=0;
int I2Cdata[4];

void setup()
{
 Serial.begin(9600);
 
 Wire.begin();  // nun mach schon
}

void loop()
{
int t = (temp - 55); // Mathematisch voll korrekte Rückrechnung der Temperatur
   Wire.requestFrom(2,4);
   
   I2Cdata[0]=Wire.read();
   I2Cdata[1]=Wire.read();
   I2Cdata[2]=Wire.read();
   I2Cdata[3]=Wire.read();
     
     temp = (I2Cdata[0]);
     spd = (I2Cdata[1]);
      v =  (I2Cdata[2]);
      r =  (I2Cdata[3]);
 
 
 Serial.print("Temperatur = ");
 Serial.println(t);
 Serial.print("Geschwindigkeit= ");
 Serial.println(spd);
 Serial.print("Spannung = ");
 Serial.println(v);
 Serial.print("Drehzahl= ");
 Serial.println( r * 78);
 

 
 delay(1000);  //nur zum gemütlichen ablesen des Seriellen Monitors
 
}

Am Seriellen Monitor des Empfängers sieht es jetzt so aus:

Temperatur = -23
Geschwindigkeit= 126
Spannung = 136
Drehzahl= 12480

Nochmal Danke für das zurechtrücken. Ich hatte mir einfach vorgestellt ich könne mir die Werte aus dem Text selektieren was eine Sackgasse war.

Das implementieren in meinen Code sollte kein Problem darstellen.