Anfänger - I2C- Senden von mehreren Analogwerten als String vom Slave zum Master

Guten Abend,
nun konnte ich ja wirklich sehr viele Fragen mit Hilfe der lieben Suchmaschinen und des Forums lösen, aber jetzt stoße ich an meine Grenzen.
Es geht wie schon der Titel sagt und das versenden von mehreren Analogwerten.
Zur Erklärung:
ich möchte in meinem Haus eine Art Bus-System mit mehreren Stationen zur Werterfassung installieren, um diese Daten nachher zur Weiterverarbeitung zu nutzen. Diese Werte setzen sich aus Helligkeit, Temperatur, Luftfeuchte zusammen. Nun habe ich mich schon so weit reingedacht das ich es hinbekommen das der Slave die Daten sammelt und zu einem String zusammenfast, laut der Serial Ausgabe klappt dies. Jetzt habe ich nur das Problem sie mit dem Master zu empfangen und zu zerlegen.

Code des Slave:

#include <Wire.h>


int LED = 8;

int LDR = A0;
int WERTLDR;

int TEMP = A1;
int WERTTEMP;

String LDRstring = "";
String TEMPstring = "";

String Sendersumme = "";


void setup()
{
  Wire.begin(2);                // 
  Wire.onRequest(requestEvent); // 
  Serial.begin(9600);
}

void loop()
{
delay(100);
digitalWrite(LED,HIGH);
delay(100);
digitalWrite(LED,LOW);


WERTLDR=map(analogRead(LDR),0,1023,0,100);
WERTTEMP=map(analogRead(TEMP),0,1023,0,100);

LDRstring = String(WERTLDR, DEC);
TEMPstring = String(WERTTEMP, DEC); 

Sendersumme = String(LDRstring + "-" + TEMPstring);
 
 
Serial.println(Sendersumme);
Serial.println("  ");
delay(100);

                       
}


void requestEvent()
{
  
 char buffer[3];
 Sendersumme.toCharArray(buffer, 3);
 Wire.write(buffer);
                                     
                       
}

Code des Masters:

#include <Wire.h>



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

void loop()
{
  Wire.requestFrom(2, 6);    // 

  while(Wire.available())    // 
  { 
   char c = Wire.read(); // 
    Serial.println(c);         // 

  }
  
  delay(100);
}

Ich vermute mal das das Problem irgendwo mit den bytes zusammenhängt.

Vielen Dank schon mal im voraus für eure Hilfe.

Warum sendest du nur 3 byte, wenn der Master 6 byte haben will und was hat das mit der fürchterlichern Stringwurstelei im Slave zu tun ?

Hast du kein Problem mit der Stellenzahl deiner WERTLDR und WERTTEMP ? Sendersumme kann alles mögliche von "0-0" bis "100-100" sein.

Warum gibst du die übertragenen Bytes im Master untereinander aus ? Das "Gegenstück" zu read ist übrigens write

So würdest du genau sehen, was der Slave sendet

while (Wire.available())
  Serial.write(Wire.read());
Serial.println();

Ich würde per I2C binär übertragen:

...
byte WERTLDR;   // 0 .. 99
byte WERTTEMP; // 0 .. 99
void loop()
{
  ... 
  WERTLDR=map(analogRead(LDR),0,1024,0,100);  // richtig auf 0 .. 99 skalieren
  WERTTEMP=map(analogRead(TEMP),0,1024,0,100);
  ...
}
void requestEvent()
{
  Wire.write(WERTLDR);
  Wire.write(WERTTEMP);                                     
}

// Master

void loop()
{
  Wire.requestFrom(2, 2);    // request 2 bytes from slave Addr2
  int ldr = Wire.read();
  int temp = Wire.read();
  Serial.print ( "LDR = ");Serial.print (ldr); 
  Serial.println ( "    TEMP = ");Serial.print (temp);
  delay(1000);
}

Hallo michael_x,
danke für die schnelle Antwort.
Ja diese Variante habe ich vorher auch schon versucht nur dort entsteht das Problem das immer nur ein Wert übertragen wird. Darum habe ich ja die String Variante gewählt um es alles zusammen zu packen.

Wenn ich also nun folgenden Code in den Slave packe:

#include <Wire.h>


int LDR = A0;
int TEMP = A1;


void setup()
{
  Wire.begin(2);                // 
  Wire.onRequest(requestEvent); // 
  Serial.begin(9600);
  
  
}

byte WERTLDR;   // 0 .. 99
byte WERTTEMP; // 0 .. 99

void loop()
{

  WERTLDR=map(analogRead(LDR),0,1024,0,100);  // richtig auf 0 .. 99 skalieren
  WERTTEMP=map(analogRead(TEMP),0,1024,0,100);
                       
}

void requestEvent()
{
   Wire.write(WERTLDR);
  Wire.write(WERTTEMP);               
                       
}

und folgenden Code in den Master:

#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(2, 2);    // request 2 bytes from slave Addr2
 
  int ldr = Wire.read();
  int temp = Wire.read();
  
  Serial.print ( "LDR = ");Serial.print (ldr); 
  Serial.print ( "  TEMP = ");Serial.println (temp);
  delay(1000);
}

erhalte ich folgende Serielle Ausgabe:

LDR = 3   TEMP = 255
LDR = 3   TEMP = 255
LDR = 4   TEMP = 255
LDR = 4   TEMP = 255
LDR = 4   TEMP = 255
LDR = 4   TEMP = 255
LDR = 3   TEMP = 255
LDR = 3   TEMP = 255

hier ändern sich nur die Werte der LDR und nicht die Werte der Temperatur.
Es wird also nur ein Wert übertragen.

Was auch sehr interessant ist das die LDR ja über den Analogeingang 0 abgefragt wird und auch dann als WERTLDR verschickt wird, und im Master als erstes abgefragt wird. Wenn ich jetzt die LDR ins Licht halte sollte sich also der Wert ändern, tut er aber nicht. Wenn ich die Hand an den Temperatur Sensor halte dann ändert sich der Wert…

Hallo Zusammen

da ich mich gerade mit einem ähnlichen Problem befasse möcht ich mich mal einklinken. Folgendes habe ich gefunden: http://www.billporter.info/2011/05/30/easytransfer-arduino-library/ Da gibts auch eine Lib für I2C . Auf http://macherzin.net/article100-Serielle-Kommunikation-per-EasyTransfer wird auch darauf eingegangen. Leider ist das Beispiel auf einen Mega2560 zugeschnitten und ich habs noch nicht geschaft es umzuschreiben. Eventuell können wir zusammen da was erreichen.

Grüße Rudi

ja, 255 (entspricht -1) , wird geliefert, wenn bei read() kein Wert vorhanden ist. Zumal wenn du nur Werte im Bereich 0 .. 99 sendest.

Wenn ich jetzt die LDR ins Licht halte sollte sich also der Wert ändern, tut er aber nicht. Wenn ich die Hand an den Temperatur Sensor halte dann ändert sich der Wert....

int LDR = A0; int TEMP = A1; da solltest du mal nach den Anschlüssen schauen, messen, und auf dem erfassenden Arduino Testausgaben einbauen.

Anderes Problem. I2C ist nicht als sternförmiges Bussystem und nicht für große Distanzen gemacht (> 2m). Ein Bussystem im Haus ist nur unter einigen Verrenkungen, sprich I2C Hubs und I2C Extender machbar. Nimm ein anderes Bussystem zB RS485.

Grüße Uwe

Ja, über große Distanzen ist I2C nicht geeignet. Das wurde entwickelt um ICs innerhalb eines Gerätes zu verbinden.

Ich habe das aber mal ausprobiert und hatte die gleichen Probleme damit mehr als ein Byte auszulesen. Sehr seltsam.

Dann habe ich stur Byte für Byte gemacht und auch den Slave so implementiert wie man es von einem IC kennt: eine Reihe Register, und einen Lesezeiger den man setzten kann, aber der auch automatisch inkrementiert und auf 0 überläuft.

Darüber dann einfach eine Funktion gelegt die zwei Bytes nacheinander ausliest.

Slave:

#include <Wire.h>

const int SLAVE_ADR = 2;
const int NUMBER_OF_REGISTERS = 4;

byte registers[NUMBER_OF_REGISTERS]; //Array für Daten
int index;

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

  setValue(0, 30);
  setValue(1, 1012);
}

void loop()
{
}

void requestEvent()
{
  Wire.write(registers[index]); //aktuellen Index auf Bus schreiben
  index = (index + 1) % NUMBER_OF_REGISTERS;    //Index inkrementieren
}

void receiveEvent(int numBytes)
{
  int index = Wire.read(); //Index lesen
  if (index < NUMBER_OF_REGISTERS) //setzen wenn im Array
    ::index = index;
  else
    ::index = 0; //wenn außerhalb auf 0 setzen
}

void setValue(int index, int value)
{
  registers[index * 2] = value >> 8;
  registers[index * 2 + 1] = value;
}

Master:

#include <Wire.h>

const int SLAVE_ADR = 2;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
}

void loop()
{
  Serial.println(F("alle Bytes sequentiell von Index 0 lesen"));
  setIndex(SLAVE_ADR, 0);
  Serial.println(readByte(SLAVE_ADR));
  Serial.println(readByte(SLAVE_ADR));
  Serial.println(readByte(SLAVE_ADR));
  Serial.println(readByte(SLAVE_ADR));
  Serial.println();

  Serial.println(F("Index auf 3 setzen und ein Byte lesen"));
  setIndex(SLAVE_ADR, 3);
  Serial.println(readByte(SLAVE_ADR));
  Serial.println();

  Serial.println(F("zwei Werte als int lesen"));
  Serial.println(readValue(SLAVE_ADR, 0));
  Serial.println(readValue(SLAVE_ADR, 1));
  Serial.println(F("-----"));
  delay(5000);
}

//ein Byte anfordern und lesen
int readByte(int adr)
{
  Wire.requestFrom(adr, 1);
  return Wire.read();
}

//Index an Slave senden
void setIndex(int adr, byte index)
{
  Wire.beginTransmission(adr);
  Wire.write(index);
  Wire.endTransmission();
}

int readValue(int adr, int index)
{
  setIndex(adr, index * 2);
  return readByte(adr) << 8 | readByte(adr);
}

.... also das definitiv werte anliegen habe ich schon festgestellt, ob nun über Serial oder auch wenn ich meine String Variante anwende.....da kommen jedenfalls werte über...aber wie Serenifly ja schon schreibt schein ich mit dem Problem nicht allein zu sein.

@ Serenfly: der Code interessiert mich dann mal und ich werde mich da heute Abend mal dransetzen.

@ uwefed: heißt das etwa für mich ich sollte mich dann doch besser in das andere Bussystem reindenken?

Grüße Sebastian

Herbers1701: @ uwefed: heißt das etwa für mich ich sollte mich dann doch besser in das andere Bussystem reindenken?

Ja. I2C ist dafür nichts.

Für RS485 gibt es Transceiver ICs wie den MAX485. Da gab es hier schon einige Themen dazu. Die Schaltung ist aber auch aus dem Datenblatt ersichtlich.

Da kann man dann Daten einfach per SoftSerial übertragen. Die Adressierung muss man dann per Hand machen. Also als erstes Byte die Adresse schicken.

Ja nur schade da brauch ich wieder ein Zusatzmodul (zb MAX485) vor jeden Arduino oder Atmega.