Habe eine blöde Frage, aber irgendwie nichts gescheites gefunden als Beispiel.
Ich habe einen Master und zwei Slaves mit I2C verbunden. Nun möchte ich gerne, dass an einem I2C Display Daten angezeigt werden, die gesendet wurden. Dafür kann ja nur mal direkt vom Master an das Display ein "ich habe das übertragen" gesendet werden. Wie aber kann ich von den Slaves an das Display übertragen, was gesendet wurde, resp. was bei den Slaves vom Master empfangen wurde? Geht das direkt vom Slave aus oder muss ich zurück zum Master und vom Master dann ans Display senden?
Hat mir da vielleicht jemand ein Beipspiel wie das geht?
Am I2C Bus kann jeder master oder slave sein, das ist keine feste Zuordnung. Oft vereinfacht es aber die Kommunikation, wenn man einen Master die Slaves einzeln abfragen läßt, dann gibt es kein Durcheinander auf dem Bus.
Im Zweifelsfall kann das Display einem Arduino zugeordnet werden, dem die anderen ihre Daten schicken. Dann bleibt der eine immer auf Empfang und gibt die Daten ans Display weiter.
würde ich auch so machen: Einem "Master" die Kontrolle über das Display geben. Wenn der neuere Werte von einem Slave haben will, dann soll er halt diesen Slave abfragen und anschließend die Ausgabe am Display aktualisiern. Weiters: das Display wird ja auch irgend eine Art von Initialisierung brauchen, das soll halt auch nur der Master machen.
Ok, ich habe glaube ich mal wieder ein wenig vergessen, dass auch wenn das Display beim Slave an der I2C Leitung hängt, dieses trotzdem direkt vom Master angesprochen werden kann. Der I2C Bus ist ja ein Bus und unabhängig wer wo dran hängt, das regeln ja die Adressen!
Aber das heisst grundsätzlich muss dann der Master immer vom Slave eine Rückmeldung abfragen, die der Master dann ans Display senden kann, richtig? Oder habe ich jetzt was falsch verstanden?
Weil es 24 Servos, 1 MP3 Shield, drei Bürstenmotoren mit Controllern, Voltmeter, ein Xbee und LED's ansteuert werden. Jetz kommt noch ein Display dazu wenn möglich. Der Grund war mal, weil ich die VarSpeedServo Lib genutzt hatte, weil diese die Geschwindigkeit der Servos bequem einstellen lässt und nur bis zu 8-10 Servos betreiben lässt. Wenns mehr werden dann macht das die Lib nicht mehr mit, sprich es gibt verrückte Bewegungen der Servos die nicht sein sollten. Und der zweite Grund war, weil "gleichzeitig" MP3 Sounds abgespielt werden sollten während die Servos bewegt werden. Dies geht nicht so gut mit nur einem Arduino und verzögert das Ganze.
Das ist wahr! Vermutlich so auch das einfachste es handzuhaben. Ich denke beim I2C immer gleich wie beim UART seriellen Port TX/RX. Darum dachte ich gestern au weia, das wird kompliziert weil der Slave dann dem Master Bescheid geben muss, der Master dem nächsten Slave und von diesem Slave dann zum Display! Das ist ja der riesen Vorteil beim I2C Bus, egal wo was angeschlossen ist, es wird über die vergebenen Adressen angesprochen und gesteuert
Ich habe mal versucht was zusammen zu stellen. Ich erhalte noch folgende Fehlermeldungen im Master Code:
Zeile #39 kann receivedData[0] nicht mit einem integer '#' nicht verglichen werden. Wie kann man dies sonst lösen?
Zeile #44 das requestData == false; wird nicht benötigt, da es nie NULL sein kann. Muss ich dies nicht auf false setzen, da sonst die if-Schleife immer durchlaufen wird, weil es auf true bleibt bis die nächste Abfrage gemacht wird?
Hier der Master:
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#define OLED_RESET -1 //reset the display with the Arduino
SSD1306AsciiWire i2cDisplay;
byte receivedData[2];
byte error = 0;
byte e = 0;
void setup()
{
Wire.begin();
#if RST_PIN >= 0
i2cDisplay.begin(&Adafruit128x64, 0x3C, OLED_RESET);
#else
i2cDisplay.begin(&Adafruit128x64, 0x3C);
#endif
#if INCLUDE_SCROLLING == 0
#error INCLUDE_SCROLLING must be non-zero. Edit SSD1306Ascii.h
#endif // INCLUDE_SCROLLING
// Set auto scrolling at end of window.
i2cDisplay.setScrollMode(SCROLL_MODE_AUTO);
i2cDisplay.setFont(Stang5x7);
i2cDisplay.setTextSize(1); //set the text size to 1 (size 1 = 7 pixels)
i2cDisplay.clearDisplay();
}
void loop()
{
requestData( receivedData, sizeof(receivedData), 9);
if(requestData == true && receivedData[0] == '#'){
error = receivedData[1];
for(byte i = 0; i < sizeof(receivedData); i++){
receivedData[i] = '\0';
}
requestData == false;
}
if(error != 0){
if(error == 1){
i2cDisplay.setCursor(0, e);
i2cDisplay.print("Command error: ");
i2cDisplay.println(error);
i2cDisplay.println("Data too long to fit in transmit buffer!");
i2cDisplay.clearToEOL();
e++;
}
else if(error == 2){
i2cDisplay.setCursor(0, e);
i2cDisplay.print("Command error: ");
i2cDisplay.println(error);
i2cDisplay.println("Received NACK on transmit of address!");
i2cDisplay.clearToEOL();
e++;
}
else if(error == 3){
i2cDisplay.setCursor(0, e);
i2cDisplay.print("Command error: ");
i2cDisplay.println(error);
i2cDisplay.println("Received NACK on transmit of data!");
i2cDisplay.clearToEOL();
e++;
}
else if(error == 4){
i2cDisplay.setCursor(0, e);
i2cDisplay.print("Command error: ");
i2cDisplay.println(error);
i2cDisplay.println("Other error!");
i2cDisplay.clearToEOL();
e++;
}
}
}
bool requestData( byte *pData, byte bytes, byte address)
{
bool success = false;
byte n = Wire.requestFrom( address, bytes);
if( n == bytes){
Wire.readBytes( pData, bytes);
success = true;
}
return( success);
}
Soll ich in diesem Fall direkt die boolsche "success" vor dem Setup definieren und sie oben mit if() abfragen und danach auf false setzen? Dann wäre es unten void requestData().
Lustigerweise ist jetzt mit dem hinzufügen der bool success = requestData(...); auch der Fehler des Vergleichs receivedData[0] == '#' weg! Spielt das nun eine Rolle oder kann das so stehen bleiben?
Master Code nun so ohne Fehlermeldungen:
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#define OLED_RESET -1 //reset the display with the Arduino
SSD1306AsciiWire i2cDisplay;
byte receivedData[2];
byte error = 0;
byte e = 0;
void setup()
{
Wire.begin();
#if RST_PIN >= 0
i2cDisplay.begin(&Adafruit128x64, 0x3C, OLED_RESET);
#else
i2cDisplay.begin(&Adafruit128x64, 0x3C);
#endif
#if INCLUDE_SCROLLING == 0
#error INCLUDE_SCROLLING must be non-zero. Edit SSD1306Ascii.h
#endif // INCLUDE_SCROLLING
// Set auto scrolling at end of window.
i2cDisplay.setScrollMode(SCROLL_MODE_AUTO);
i2cDisplay.setFont(Stang5x7);
i2cDisplay.setTextSize(1); //set the text size to 1 (size 1 = 7 pixels)
i2cDisplay.clearDisplay();
}
void loop()
{
bool success = requestData( receivedData, sizeof(receivedData), 9);
if(success == true && receivedData[0] == '#'){
error = receivedData[1];
for(byte i = 0; i < sizeof(receivedData); i++){
receivedData[i] = '\0';
}
}
if(error != 0){
if(error == 1){
i2cDisplay.setCursor(0, e);
i2cDisplay.print("Command error: ");
i2cDisplay.println(error);
i2cDisplay.println("Data too long to fit in transmit buffer!");
i2cDisplay.clearToEOL();
e++;
}
else if(error == 2){
i2cDisplay.setCursor(0, e);
i2cDisplay.print("Command error: ");
i2cDisplay.println(error);
i2cDisplay.println("Received NACK on transmit of address!");
i2cDisplay.clearToEOL();
e++;
}
else if(error == 3){
i2cDisplay.setCursor(0, e);
i2cDisplay.print("Command error: ");
i2cDisplay.println(error);
i2cDisplay.println("Received NACK on transmit of data!");
i2cDisplay.clearToEOL();
e++;
}
else if(error == 4){
i2cDisplay.setCursor(0, e);
i2cDisplay.print("Command error: ");
i2cDisplay.println(error);
i2cDisplay.println("Other error!");
i2cDisplay.clearToEOL();
e++;
}
}
}
bool requestData( byte *pData, byte bytes, byte address)
{
bool success = false;
byte n = Wire.requestFrom( address, bytes);
if( n == bytes){
Wire.readBytes( pData, bytes);
success = true;
}
return( success);
}
Edit: Irgendwo liegt noch ein Fehler. Das Display zeigt nichts an. Nochmals durchgehen wo etwas ist
Edit: Wenn ich am Display ein paar Ausgaben einfüge, dann sehe ich, dass er die bytes wohl empfängt, aber die Bedingung receivedData[0] nicht gleich '#' ist. Auch wenn ich es als byte('#') deklariere ändert es weder an der Compiler-Ausgabe etwas, noch durchläuft er die if-Schlaufe. Sieht hier jemand einen Fehler?
Edit 2:
Habe den Fehler glaube ich gefunden! Beim Slave sollte in der Funktion displayError(); das requestEvent(); aufgerufen werden damit es sendet, oder?
Hier noch die vollständigen Codes. Habe jeweils 2sek. eingefügt um die empfangenen Daten lesen zu können. Die Befehle i2cDisplay.clearToEOL(); braucht man scheinbar nicht um die letzte Linie zu löschen.
Master:
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#define OLED_RESET -1 //reset the display with the Arduino
SSD1306AsciiWire i2cDisplay;
byte receivedData[2];
byte error = 0;
byte e = 0;
void setup()
{
Wire.begin();
#if RST_PIN >= 0
i2cDisplay.begin(&Adafruit128x64, 0x3C, OLED_RESET);
#else
i2cDisplay.begin(&Adafruit128x64, 0x3C);
#endif
#if INCLUDE_SCROLLING == 0
#error INCLUDE_SCROLLING must be non-zero. Edit SSD1306Ascii.h
#endif // INCLUDE_SCROLLING
// Set auto scrolling at end of window.
i2cDisplay.setScrollMode(SCROLL_MODE_AUTO);
i2cDisplay.setFont(Stang5x7);
i2cDisplay.setTextSize(1); //set the text size to 1 (size 1 = 7 pixels)
i2cDisplay.clearDisplay();
}
void loop()
{
bool success = requestData( receivedData, sizeof(receivedData), 9);
if(success == true && receivedData[0] == '#'){
error = receivedData[1];
for(byte i = 0; i < sizeof(receivedData); i++){
receivedData[i] = '\0';
}
}
delay(2000);
if(error != 0){
if(error == 1){
i2cDisplay.setCursor(0, e);
i2cDisplay.print("Command error: ");
i2cDisplay.println(error);
i2cDisplay.println("Data too long to fit ");
i2cDisplay.println("in transmit buffer!");
i2cDisplay.clearToEOL();
e = e + 3;
}
else if(error == 2){
i2cDisplay.setCursor(0, e);
i2cDisplay.print("Command error: ");
i2cDisplay.println(error);
i2cDisplay.println("Received NACK on ");
i2cDisplay.println("transmit of address!");
//i2cDisplay.clearToEOL();
e = e + 3;
}
else if(error == 3){
i2cDisplay.setCursor(0, e);
i2cDisplay.print("Command error: ");
i2cDisplay.println(error);
i2cDisplay.println("Received NACK on ");
i2cDisplay.println("transmit of data!");
//i2cDisplay.clearToEOL();
e = e + 3;
}
else if(error == 4){
i2cDisplay.setCursor(0, e);
i2cDisplay.print("Command error: ");
i2cDisplay.println(error);
i2cDisplay.println("Other error!");
//i2cDisplay.clearToEOL();
e = e + 2;
}
}
}
bool requestData(byte *pData, byte bytes, byte address)
{
bool success = false;
byte n = Wire.requestFrom(address, bytes);
if( n == bytes){
Wire.readBytes(pData, bytes);
success = true;
}
return(success);
}
Ich bin nicht sicher, ob ich Dich richtig verstehe.
Normalerweise fragt der Master beim Slave nach neuen Daten, der Slave kann sich aber nicht von sich aus beim Master melden "Ich habe neue Daten!".
Ich habe das mal mit einer Meldeleitung gelöst: Master (Eingang) und Slave (Ausgang) sind miteinander verbunden. Hat der Slave nun neue Daten, meldet er dies über die Meldeleitung, woraufhin der Master aktiv werden kann. Der Master vermeidet dadurch unnötige Abfragen, neue Informationen werden dennoch umgehend verarbeitet.
Meine Meldeleitung wird auch gerne als Interruptverbindung bezeichnet, wobei ich bei mir keinen Vorteil sah, einen Interrupt zu nutzen.