Két Arduino összekapcsolása I2C-vel

Előfordulhat, hogy két Arduino között kel adatokat cserélni. Az alábbi megoldást az egy interneten megjelenő cikkben találtam. Sajnos a forrást már nem tudom megjelölni. Az összeköttetés létrehozásának alapja, hogy az Arduino panelek A4 és A5 kivezetéseit kössük össze. Nem szabad elfelejteni a föld vezetéket sem, ha az Arduino panelek egymástól független táplálásúak. Ha ugyanarra a tápegységre kötjük mindkettőt, akkor a közös föld egyértelmű.

Az egyik Arduino lesz a mester, aki küldi az adatokat, míg a másik a slave, aki fogadja. Mivel az I2C buszon több slave eszköz is lehet, a slave eszközöknek önálló és megkülönböztethető címmel kell rendelkeznie. Az RTL óra panel esetében pl. ez a cím egy gyárilag beállított cím, amit „huzalozással” meg is lehet változtatni. Az Arduino esetén a címet szoftveresen kell beállítani.

Elméletileg az I2C buszon több master is lehet. Sajnos nem találtam arra vonatkozóan semmilyen információt, hogy az I2C busz kezelésére szolgáló függvény könyvtárak tudják-e kezelni a két mastert a buszon. Valószínűnek tartom, hogy nem. Azonban nem kizárt, hogy a wire.begin() (masterterként működik) és a Wire.begin(8) (8-as című slave-ként működik) utasításokat lehet egy programon belül váltogatni, és egy Arduino szerepe egy programon belül váltogatható. Persze adminisztrálni kell, hogy milyen szerepben van a másik Arduino, nehogy ketten egyszerre legyenek masterek. Ezt nem próbáltam ki eddig.

További észrevételem, hogy a Wire.beginTransmission(8);  és a Wire.endTransmission(); programsorok között kiadható Wire.Write()-ok száma korlátozott. Úgy tapasztaltam, hogy 32 byte vihető át egyszerre. Egy programban ennél több infót kelett átvinnem, ezért kénytelen voltam részekre bontani az átvitelt. Ekkor a Wire.beginTransmission(8) után következő buszra kiírt byte egy jelző byte volt, ami a slave-nak megmondta, hogy éppen melyik blokkot viszem át. A slave oldalon az adatok fogadására készített függvény ebből tudta, hogy az érkező adatokkal mely változókat kell tölteni!

Az alábbi példa programban a slave egy analóg bemeneten mért értéket long változóba olvas be. Direkt long, hogy látható legyen, hogyan kell byte-onként átvinni az értéket.  Amikor a master lekérdezte a slave-től az adatot, összerakja a byte-onként érkezett tartalmat egy long változóba és kiírja a soros monitorra, majd lássuk mi jött. A fogadott értékeket visszaküldi a slave-nak, hogy erre is lássunk példát.

A master programja

/*
 * Ez a program demo két arduino I2C porton keresztül történő adatcseréjének bemutatására készült.
 * Ez itt a master programja. A mester másodpercenként adatot kér a slave-től, ami egy -500 és 523 közötti
 * long számot bont fel és byte-onként elküld. A mester programja összerakja a 4 byte-ot egy long
 * változóba és kiírja, valamint a 4 byte-ot azonnal vissza is küldi a slave-nak!
 */
#include<Wire.h>           
byte fogad1;  //ebbe a vátozóba olvassuk be a slave által küldött 1. byte-ot
byte fogad2;  //ebbe a vátozóba olvassuk be a slave által küldött 2. byte-ot
byte fogad3;  //ebbe a vátozóba olvassuk be a slave által küldött 3. byte-ot
byte fogad4;  //ebbe a vátozóba olvassuk be a slave által küldött 4. byte-ot
void setup()
{
  Serial.begin(9600);       // soros port inicializálása            
  Wire.begin();                 // I2C kommunikáció inicializálása, nincs cím, ezért master
}
void loop()
{
    Wire.requestFrom(8,4);      // a master kér négy bájtot a slave-től
    fogad1 = Wire.read();          //beolvassuk sorban a slave által küldött byte-okat   
    fogad2 = Wire.read();           
    fogad3 = Wire.read();           
    fogad4 = Wire.read();           
    Serial.print("Slave-tol fogadott:");    //kiírjuk a soros portra a fogadott byte-okat
    Serial.print(fogad1);Serial.print(",");Serial.print(fogad2);Serial.print(",");
    Serial.print(fogad3);Serial.print(",");Serial.print(fogad4);Serial.print("->");
    //kiírjuk a soros portra a long-nak összerakott értéket
    Serial.println(byteToLong(fogad1,fogad2,fogad3,fogad4));
    Wire.beginTransmission(8);  // elindít a master egy átvitelt a 8 című slave felé
    Wire.write(fogad1);         // küldi a a byte-okat sorban a slave felé
    Wire.write(fogad2);        
    Wire.write(fogad3);                         
    Wire.write(fogad4);                         
    Wire.endTransmission();     // vége az átvitelnek
    delay(1000);                                     
}   
long byteToLong(long inp1, long inp2, long inp3, long inp4)
//Ez a funkció 4 byte-ból csinál egy long változót és visszaadja az eredményt
{
  //4 byte long változóvá alakítása
  return ((inp1 << 0) & 0xFF) + ((inp2 << 8) & 0xFFFF) + ((inp3 << 16) & 0xFFFFFF) + ((inp4 << 24) & 0xFFFFFFFF);
}


Mint az alábbi példából látható lesz, a slave programja megszakításokkal dolgozik. Fut a loop-ban egy program, és csinálja amit kell. Ezt a programot a master részéről érkező adatkérés, illetve a mastertől küldött adatküldés megszakítja, és mindig a megfelelő függvény kerül meghívásar.


Slave programja:

/*
 * Ez a program demo két arduino I2C porton keresztül történő adatcseréjének bemutatására készült.
 * Ez itt a slave programja. A master adatkérésekor megméri az A0 portra kötött feszültséget
 * beolvassa egy long változóba, azt felbontja 4 byte-ra és byte-onként elküldi a masternek.
 * A mester programja (vissza) küld 4 byte-ot, amit a program fogadást követen long számmá alakít
 */
#include<Wire.h>
byte fogad1;
byte fogad2;
byte fogad3;
byte fogad4;
void setup()
{ 
  Serial.begin(9600);           // soros port inicializálása          
  Wire.begin(8);                // I2C kommunikáció inicializálása 8-as eszköz címmel (mivel slave, címet kell megadni)
  Wire.onReceive(slave_fogad);  //Ezt a funkciót hívja az Arduino, amikor adatot kap a mastertől
  Wire.onRequest(slave_kuld);   //Ezt a funkciót hívja meg az Arduino, amikor a master adatot kér a slave-től
  analogReference(DEFAULT);     //A tápfeszt veszi referenciának (5V)az analóg bemenetre kötött feszültség mérésekor
}
void loop()
{  
}
void slave_fogad ()     
//Ez a függvény akkor indul, amikor a slave értesítést kap, hogy a master adatot fog küldeni
{
  //4 byte-ot várunk a mastertől, és azt olvassuk be
  fogad1 = Wire.read();           
  fogad2 = Wire.read();           
  fogad3 = Wire.read();           
  fogad4 = Wire.read();
  //kiirjuk a soros portra a fogadott byte-okat          
  Serial.print("Mastertől fogadott:");    //Prints in Serial Monitor
  Serial.print(fogad1);Serial.print(",");Serial.print(fogad2);Serial.print(",");
  Serial.print(fogad3);Serial.print(",");Serial.print(fogad4);Serial.print("->");
  //visszaalakítjuk a fogadott 4 byte-ot long számmá és kiirjuk a soros portra
  Serial.println(byteToLong(fogad1,fogad2,fogad3,fogad4));
}
void slave_kuld()                            
//Ez a függvény akkor indul, amikor a master adatot kér a slave-től
{
  long kuldendo=analogRead(A0)-500; //a kipróbáláshoz az analóg bemenetet használjuk -500 és 523 között
  Serial.print("Masternek kuldve:");Serial.println(kuldendo);
  //felbontjuk 4 byte-ra a küldendő long értéket
  byte out1 = (kuldendo & 0xFF);
  byte out2 = ((kuldendo >> 8) & 0xFF);
  byte out3 = ((kuldendo >> 16) & 0xFF);
  byte out4 = ((kuldendo >> 24) & 0xFF);
  Wire.write(out1);              
  Wire.write(out2);
  Wire.write(out3);
  Wire.write(out4);
}
long byteToLong(long inp1, long inp2, long inp3, long inp4)
//Ez a funkció 4 byte-ból csinál egy long változót és visszaadja az eredményt
{
  //4 byte long változóvá alakítása
  return ((inp1 << 0) & 0xFF) + ((inp2 << 8) & 0xFFFF) + ((inp3 << 16) & 0xFFFFFF) + ((inp4 << 24) & 0xFFFFFFFF);
}

Kísérletezgetés közben jöttem rá, hogy nem mindegy hol helyezzük el a slave programjában az adatok írását ( wire.write()-okat ), mert tapasztalatom szerint kizárólag a master által kezdeményezett adatküldés kiszolgáló függvényében lehetnek. Ez a fenti példában ez a slave_kuld() függvény. Kipróbáltam olyan megoldást is, amiben a slave_kuld() függvény csak egy jelzést ad arra, hogy a master adatokat vár. Valahol a program egy másik részén akartam az adatokat elküldeni, nevezetesen a loop() ciklusban, amikor nekem megfelelő, és nem akkor amikor pont kéri a mester a slave_kuld() meghívásával. Sajnos ez így nem működött. Az adatok küldése megtörtént, de a master-hez 4db 255 tartalmú byte érkezett meg. Szóval az slave_kuld() függvénybe kell tenni a wire.write sorokat!! Elképzelhető, hogy valamit nem vettem észre, ezért még nem adom fel, de jelen tudásom szerint, csak így lehet küldeni!

A két Arduino közötti kommunikációt azért kellett megismernem, mert készítettem egy időjárás jelző állomást, aminek a vezérlő programja nem fért el egyetlen Arduino nano alaplapban. Így szükség volt kettőre, melyeknek kommunikálniuk kellett. Ha érdekel, akkor itt találod meg a leírásokat és mindkét alaplap programját.

Mennyire volt hasznos amit olvastál?

Kattints egy csillagra az értékeléshez!

Sajnálom, hogy amit olvastál nem volt hasznos számodra!

Szeretném ha elégedett lennél!

Írd le kérlek, hogy mi nem tetszett!?