Probleme beim Übertagen von mehreren Werten, vom Slave zum Master I2C

Hallo zusammen,

ich möchte gern an einen Attiny85 einen Reedschalter als Interrupt überwachen. Dabei sollen die Anzahl der Schaltvorgänge so wie die Zeit dazwischen gemessen werden. Diese Variablen, shortesIntervall und durchschnittIntervall, möchte ich circa alle 5 Minuten vom Master (ESP8266) via I2c abfragen lassen. Beide Werte sind Ganzzahlen von 10 bis 2350. Anschließend werden sie zurück gesetzt.

Die Funktion der Überwachung des Reedschalter habe ich schon, nun wollte ich mich mit der Übertragung beschäftigen. Doch leider komme ich da nicht weiter. Bei der Recherche bin ich auf diesen Forum Eintrag gestoßen.

Ich versuchte die Option mit der Union.

Als Ergebnis im Serial Monitor bekomme ich aber nicht die erwartet werte von

 data.value1 = 1;
 data.value2 = 4;
 data.value3 = 12;
 data.value4 = 30000;
 data.value5 = -10000;

sondern ich erhielt diese Werte:

-1
-1
-1
-1
-1

Die Verkabelung und das Installieren der Sketche funktioniert. Da das Beispiel Sketch von Attinycore Slaversender und Mastereader funktioniert. Nun weis ich nicht wo der Fehler ist oder sein kann.

Danke schon mal für eure Hilfe
Gruß

sollte Verdacht auf Code fallen, könntest du ihn uns zum Prüfen posten. Beide Sketches.
Und falls du Interesse hast die Schematik zu zeigen, wird sie auch geprüft.

Hast du am Bus Pullup-Widerstände dran und wie lang ist dein Kabel zwischen dem Master und Slave ?
Das wir deine Sketche benötigen, wurde ja schon geschrieben.

Also der Aufbau sieht ähnlich wie das Bild aus. Ohne LED mit Widerstand und ohne Gelbe Leitung.

Der Master-code

#include <Wire.h> 

const int SLAVE_ADR = 2;

union data_u
{
  struct
  {
    int value1;
    int value2;
    int value3;
    int value4;
    int value5;
  };
  byte bytes[10];
};

data_u data;

void setup()
{
  Serial.begin(9600);
  Serial.println("Master");
  Wire.begin();

  getData();
}

void loop()
{
}

void getData()
{
  Wire.requestFrom(SLAVE_ADR, sizeof(data));
  for (unsigned int i = 0; i < sizeof(data); i++)
    data.bytes[i] = Wire.read();

  Serial.println(data.value1);
  Serial.println(data.value2);
  Serial.println(data.value3);
  Serial.println(data.value4);
  Serial.println(data.value5);
}

und der Slave Code

#include <Wire.h> 

const int SLAVE_ADR = 2;

union data_u
{
 struct
 {
 int value1;
 int value2;
 int value3;
 int value4;
 int value5;
 };
 byte bytes[10];
};

data_u data;

void setup()
{
 data.value1 = 1;
 data.value2 = 4;
 data.value3 = 12;
 data.value4 = 30000;
 data.value5 = -10000;
 
 Wire.begin(SLAVE_ADR);
 Wire.onRequest(requestEvent);
}

void loop()
{
}

void requestEvent()
{
 Wire.write(data.bytes, sizeof(data));
}

Das Beispiel sketch das Auch funktioniert ist folgendes.

// Wire Master Reader
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Reads data from an I2C/TWI slave device
// Refer to the "Wire Slave Sender" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#include <Wire.h>

void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() {
  Wire.requestFrom(8, 6);    // request 6 bytes from slave device #8

  //while (Wire.available()) { // slave may send less than requested
    char c = Wire.read(); // receive a byte as character
    Serial.print(c);         // print the character
  //}

  delay(500);
}

und

// Wire Slave Sender
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Sends data as an I2C/TWI slave device
// Refer to the "Wire Master Reader" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#include <Wire.h>

void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event
}

void loop() {
  delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  Wire.write("hellos "); // respond with message of 6 bytes
  // as expected by master
}

aktuell ist alles noch auf ein Breakboard daher eher kurze Kabel <10cm

ein µC ist 8 Bit, andere 32Bit, und int beinhalet unterschiedliche Anzahl von Bytes

Das kann nicht funktionieren.
Es fehlt am Attiny die Versorgungsspannung und die Abblockkondensatoren.

probier mal so:

union data_u
{
  struct
  {
    int16_t value1;
    int16_t value2;
    int16_t value3;
    int16_t value4;
    int16_t value5;
  };
  uint8_t  bytes[10];
};

Adressen bis 8 sind reserviert für spezielle Funktionen. Das dürfte der hauptsächliche Grund für die -1 im Seriellen Monitor sein.

Das dürfte beim ATtiny 16 Bit sein, beim NodeMCU mötlicherweise 32 Bit. Bitte unbedingt einen hart typisierten Typ verwenden, also int16_t oder uint16_t.

Hier abfragen, ob tatsächlich was angekommen ist.

Also ich habe nun mal in beiden Sketches die int durch int16_t ersetzt. Die Adresse habe ich auf 12 gesetzt.
Ich habe ein Serial.println(sizeof(data)); direkt unter Wire.requestFrom(SLAVE_ADR, sizeof(data)); hinzugefügt.

Im Seriellmonitor bekomme ich immer noch.

sizeof(data) = 10
-1
-1
-1
-1
-1

Was liefert denn Wire.requestFrom() zurück? Vertrauen ist gut, Kontrolle ist besser!

1 Like

Kannst du bitte nochmal erklären, wie es mit deinem Schaltbild in Post #4 funktionieren soll ?
Was fehlt habe ich schon geschrieben.
Zeige uns doch bitte ein richtiges, aktuelles Schaltbild.

Hab mal den aktuellen Schaltplan skizziert.

Attiny85 --- Esp8266
Pin 4 --- Gnd
Pin 5 --- D2
Pin 7 --- D1
Pin 8 --- vcc

Pullups von vcc auf SDA und SCL jeweils 4.7kohm
Keramikkondesator 100nf vcc zu gnd

Datenaustausch über "union type punning" ist zwischen identischen µC mit identischem gcc Compiler möglich!

In allen anderen Fällen eher ein "No Go!"
So auch hier.

Gründe/Stichworte:

  1. big vs. little Endian
  2. alignment
  3. Padding
  4. Damit auch sizeof(datentype)
  5. Einer- vs. Zweierkomplement

Es fällt also insgesamt in den Bereich: "Implementation defined behavior"
Im C++ Standard gilt es gar als "undefined behavior"

1 Like

das würde man sicher sehen können :

#include <Wire.h> 

const int SLAVE_ADR = 2;

union data_u
{
  struct
  {
    int value1;
    int value2;
    int value3;
    int value4;
    int value5;
  };
  byte bytes[10];
};

data_u data;

void setup()
{
  Serial.begin(9600);
  Serial.println("Master");
  Wire.begin();

  getData();
}

void loop()
{
}

void getData()
{
  Wire.requestFrom(SLAVE_ADR, sizeof(data));
  for (unsigned int i = 0; i < sizeof(data); i++)
  {
    uint8_t Byte = Wire.read();
    Serial.println(Byte , HEX);
    data.bytes[i] = Byte ;
    Serial.println(data.bytes[i], HEX);
  }
  Serial.println(data.value1);
  Serial.println(data.value2);
  Serial.println(data.value3);
  Serial.println(data.value4);
  Serial.println(data.value5);
}

Bei verschiedenen Microcontroller würde ich die zu übertragenen Bytes eigenhändig der Übertragung zuordnen. Dann weis man wofür welches Byte genutzt wird.

Den Hinweis auf Type punning hat eh schon @combie aufgezeigt.

Das funktioniert bei mir mit einem NodeMCU und einem Nano:

Master

/*
   I2C Master - Structure
   https://forum.arduino.cc/t/probleme-beim-ubertagen-von-mehreren-werten-vom-slave-zum-master-i2c/1274438
   by noiasca
*/
#include <Wire.h>

const uint8_t SLAVE_ADR = 12;

struct Payload {
  int16_t valueA;
  int16_t valueB;
  int16_t valueC;
  int16_t valueD;
  int16_t valueE;
} ;

Payload data;
uint8_t buffer[sizeof(data)];

void setup() {
  Serial.begin(9600);
  Serial.println("Master");
  Serial.print("SDA "); Serial.println(SDA);
  Serial.print("SCL "); Serial.println(SCL);
  Wire.begin();

  getData();
}

void loop() {
  delay(5000);
  getData();
}

void getData() {
  int result = Wire.requestFrom(SLAVE_ADR, sizeof(data));
  if (result) {
    for (unsigned int i = 0; i < sizeof(data); i++) {
      buffer[i] = Wire.read();
      Serial.print(buffer[i], HEX);
      Serial.print(' ');
    }
    Serial.println();

    data.valueA = (int16_t)buffer[0] << 8 | buffer[1];
    data.valueB = (int16_t)buffer[2] << 8 | buffer[3];
    data.valueC = (int16_t)buffer[4] << 8 | buffer[5];
    data.valueD = (int16_t)buffer[6] << 8 | buffer[7];
    data.valueE = (int16_t)buffer[8] << 8 | buffer[9];

    Serial.println(data.valueA);
    Serial.println(data.valueB);
    Serial.println(data.valueC);
    Serial.println(data.valueD);
    Serial.println(data.valueE);
  }
  else {
    Serial.print("result="); Serial.println(result);
  }
}
//

und Slave

/*
   I2C Slave - Structure
   https://forum.arduino.cc/t/probleme-beim-ubertagen-von-mehreren-werten-vom-slave-zum-master-i2c/1274438

   by noiasca
   
*/
#include <Wire.h>

const uint8_t SLAVE_ADR = 12;

struct Payload {
  int16_t valueA;
  int16_t valueB;
  int16_t valueC;
  int16_t valueD;
  int16_t valueE;
} ;

Payload data;
uint8_t buffer[sizeof(data)];

void setup() {
  Wire.begin(SLAVE_ADR);
  Wire.onRequest(requestEvent);
}

void loop() {
}

void requestEvent() {
  static int16_t counter = 0;
  data.valueA = 1;
  data.valueB = ++counter;
  data.valueC = random(42);
  data.valueD = 30000;
  data.valueE = -10000;

  buffer[0] = data.valueA >> 8;
  buffer[1] = data.valueA & 0xFF;
  buffer[2] = data.valueB >> 8;
  buffer[3] = data.valueB & 0xFF;
  buffer[4] = data.valueC >> 8;
  buffer[5] = data.valueC & 0xFF;
  buffer[6] = data.valueD >> 8;
  buffer[7] = data.valueD & 0xFF;
  buffer[8] = data.valueE >> 8;
  buffer[9] = data.valueE & 0xFF;  

  //buffer[0] = 42;
  Wire.write(buffer, sizeof(data));
}
//

gibt am master sauber aus

10:19:37.135 -> 0 1 0 25 0 7 75 30 D8 F0 
10:19:37.169 -> 1
10:19:37.169 -> 37
10:19:37.169 -> 7
10:19:37.169 -> 30000
10:19:37.169 -> -10000

Dein Hauptproblem wird die Verkabelung/Pullups sein.

bei falscher pullup Beschaltung bekomm ich auch ein

10:02:20.253 -> FF FF FF FF FF FF FF FF FF FF 
10:02:20.307 -> -1
10:02:20.307 -> -1
10:02:20.307 -> -1
10:02:20.307 -> -1
10:02:20.307 -> -1
3 Likes

Habe ich nicht, nutze daher einen ESP32.

Programm ESP32
// ESP32 als I2C-Master
#include <Wire.h>

const int SLAVE_ADR = 12;
const int ANZAHL = 10; // Wire möchte int!

union data_u
{
  struct
  {
    int16_t value1;
    int16_t value2;
    int16_t value3;
    int16_t value4;
    int16_t value5;
  };
  byte bytes[ANZAHL];
};

data_u data;

void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(115200);  // start serial for output
  delay(500);
  Serial.println(F("Start"));
}

void loop() {
  byte a = 0;
  Wire.requestFrom(SLAVE_ADR, ANZAHL);    // request bytes from slave device

  while (Wire.available()) { // slave may send less than requested
    data.bytes[a++] = Wire.read(); // receive a byte as character
  }
  if (a)
  {
    for (byte j = 0; j < ANZAHL; j++)
    {
      if(data.bytes[j]<0x10) Serial.print('0');
      Serial.print(data.bytes[j], HEX);
      Serial.print(' ');
    }
    Serial.println();
    Serial.println(data.value1);
    Serial.println(data.value2);
    Serial.println(data.value3);
    Serial.println(data.value4);
    Serial.println(data.value5);
  }
  delay(555);
}
Programm ATtiny95
// ATMEL ATtiny45/85 als I2C-Slave
//
//                +-\/-+
//        Reset  1|    |8  Vcc
// A3 (D 3) PB3  2|    |7  PB2 (D 2) A1 SCL
// A2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1
//          GND  4|    |5  PB0 (D 0) pwm0 SDA
//                +----+
#include <Wire.h>

const int SLAVE_ADR = 12;
const int ANZAHL = 10; // Wire möchte int!

union data_u
{
  struct
  {
    int16_t value1;
    int16_t value2;
    int16_t value3;
    int16_t value4;
    int16_t value5;
  };
  byte bytes[ANZAHL];
};

data_u data;

void setup()
{
  data.value1 = 1;
  data.value2 = 4;
  data.value3 = 12;
  data.value4 = 30000;
  data.value5 = -10000;

  pinMode(PB4, OUTPUT);
  digitalWrite(PB4, HIGH);
  delay(1000);
  digitalWrite(PB4, LOW);
  delay(1000);
  Wire.begin(SLAVE_ADR);
  Wire.onRequest(requestEvent);
}

void loop()
{
  delay(100);
  data.value3 = analogRead(PB3);
  if (data.value3 > 511) {
    digitalWrite(PB4, HIGH);
  }
  else {
    digitalWrite(PB4, LOW);
  }
}

void requestEvent()  // respond with message of 10 bytes as expected by master
{
  for (byte j = 0; j < ANZAHL; j++) 
  {
    Wire.write(data.bytes[j]);
  }
}
Anzeige ESP32
14:32:44.199 -> 01 00 04 00 80 03 30 75 F0 D8 
14:32:44.199 -> 1
14:32:44.199 -> 4
14:32:44.199 -> 896
14:32:44.199 -> 30000
14:32:44.199 -> -10000
1 Like

Danke Euch allen :pray:

nun läuft die Übertragung. War wohl nicht nur ein Software Problem sondern eher eine Kombination aus Software und Hardware.
Nach dem Tipp von @noiasca bezüglich der Widerstandskraft habe ich diese nochmal nachgemessen. Waren wohl keine 4,7k sondern 47k :flushed:.

Aber danke euch allen :metal:

1 Like

Wenn man unabhängig vom gesendeten immer nur 0xff oder -1 liest, ist das nicht mit undefined behaviour zu erklären.
Wenn man was kriegt, das zwar nach Daten aussieht, aber nicht so recht passt, ist immer noch Zeit, sich über Byte-Reihenfolge und -Anzahl Gedanken zu machen.
Wenn es aber stimmt, braucht man keine Angst vor einer neuen Compiler-Version zu haben.
Ob die Lösung auf anderer Hardware läuft, ist natürlich neu zu prüfen.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.