Két Arduino összekapcsolása I2C-vel

Tartalom:

  • Két Arduino alaplap összekapcsolásának alapelve, technikai megvalósítása (vezetékezés)
  • Master és slave működés, információcsere tulajdonságai
  • Minta forráskód az adatcsere bemutatására

———————————————————————————

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áltozóba olvassuk be a slave által küldött 1. byte-ot
byte fogad2;  //ebbe a változóba olvassuk be a slave által küldött 2. byte-ot
byte fogad3;  //ebbe a változóba olvassuk be a slave által küldött 3. byte-ot
byte fogad4;  //ebbe a változó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);
}
/*
 * 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!

Szövegesen is leírhatod véleményedet!