I2C multimaster

Tartalom:

  • Több master-es I2C kialakítás lehetőségei a wire könyvtár eszközeivel
  • Mi történik, ha több master is van a buszon, adatok keveredése
  • Master funkció adogatásának alapötlete
  • Egy „vezér” master, aki slave is egyben
  • Forráskódok

Már a gyakorlatban alkalmazom két Arduino összekapcsolására és adatcserére az I2C buszt. Remekül működik, de az adatok átvitelét mindíg csak az egyik eszköz, a master tudja kezdeményezni!

Szükségem lenne egy olyan berendezésre, ami egy I2C buszon működő FRAM-ban tartja az aktuális adatokat, miközben az egyik Arduino írja, egy másik pedig olvassa az adatokat. Ennek az az értelme, hogy áramszünet esetén az FRAM nem veszíti el az adatokat, nem kell hozzá akkumulátoros táplálás. Ezt teszi egy jólnevelt eeprom is, azonban az eeprom csak néhány százezerszer írható újra és tönkremegy. Az FRAM viszont gyakorlatilag végtelen élettartamú. Az elképzelés szerint több Arduino is írogat az FRAM-ba, és a megfelelő pillanatokban bármelyik kiolvashatja a számára szükséges adatokat. Nagyképűen megfogalmazva az FRAM egy adatközpont! Jelen esetben ez egy összetett időjárás állomás, ami egy külön Arduino-val méri a szélsebességet és a csapadékmennyiséget, egy külön Arduino-val vezérli a kültéri kijelzőt, és méri dallas onewire hőmérő chip-ekkel a hőmérsékletet, és egy külön Arduino további szenzorokat kérdezget, vezérel egy helyi LCD kijelzőt és 433Mhz-s rádió adóval küldözgeti a mérési eredményeket. A három különálló Arduino-ra történelmi, sebesség és flash memória méret problémák miatt volt szükség.

Mi kell ahhoz, hogy ugyanazt az I2C eszközt több Arduino is elérje és használhassa? Az egyik megoldás szerint van egy master, aki mindenkinek tud adatokat küldeni és tőlük fogadni. Ez a master begyűjti az adatokat, és beírogatja az FRAM-ba amit sikerült összeszednie. Érezhetően bonyolult adat áramlást kell megvalósítani, ami bonyolult programot fog eredményezni. Nekem más indokom is volt, hogy ne ezt a módszert használjam. Nevezetesen a szélsebesség mérőben kén kontaktus között szerettem volna elküldeni az adatokat, de arról, hogy ez mikor van a master nem tud. Ezért lenne jó, ha a szélsebesség mérő lenne a master. Ez viszont nem lehet a master, mert a master-nek olyan sok feladata van, hogy nem működhet szélsebességmérőként is. Tehát kéne két master is az I2C buszon!

Egyébként is elegánsabb megoldás, hogy bármelyik eszköz elérhesse bármelyiket, vagyis egy több master-es megoldás. Annyit már eddig is tudtam az I2C működéséről, hogy a programomban a setup()-ban a wire.begin() fügvénnyel lehet inicializálni az I2C kezelő függvényeket. Ha ebben a függvényben nem adok meg paramétert, akkor ez egy master, ha viszont megadok paraméterben egy címet pl. wire.begin(8), akkor ez egy 8-as című slave lesz az I2C buszon. Tpasztalatom szerint ez a paraméterben megadott cím csak annyit jelent az Arduino programja számára, hogy ha az I2C buszon megszólítja valaki más ezt a címet, akkor válaszolni fog. Talán nem meglepő, hogy a beginTransmisson() függvény ekkor is elkezdi a kommunikációt annak rendje és módja szerint, ha éppen slave-nek paramétereztük a setup()-ban a wire használatát. Tehát az Arduino lehet egyszerre slave és master is.

Ki is próbáltam a dolgot. Írtam egy programot, ami egy DS3231 RTC órából kérdezgette le az időt, amilyen gyorsan csak tudta, tehát egy kiolvasás után rögtön következett a következő. Ezt a programot két Arduino-ba töltöttem be. A két Arduino-t és egy DS3231 RTC órát összekötöttem párhuzamosan az I2C buszra. Természetesen itt még a wire.begin() függvényt paraméter nélkül hívtam meg, hiszen mindkét program master, és egy RTC órát kérdezgetett. Hardveresen megy a dolog, hiszen minden master kiadhat órajelet, akár többen is egyszerre. Ekkor az  órajel nem lesz szabályos négyszögjel, hiszen „belerángatnak” egymás órajelébe. Logikailag ettől még az adatok jönnek mennek, hiszen az órajelenken lesz fel és lefutóél. A buszon az adatvezetéket többen is 0-ra rángathatják egyszerre. Ebből csak adatsérülés lesz, hardveres probléma nem. Figyeltem, hogy viselkedik a két master, hogyan birkózik meg a párhuzamos I2C busz használattal. Jelentem, nem jól! Sajnos zavarják egymást. A legbosszantóbb, hogy hibaüzenet nem keletkezik az endTransmission() függvényben, viszont mindkét master rossz időt és dátumot olvas ki az RTC órából.

Ha növeltem az RTC óra lekérdezések közötti időt, és mindkét arduino-ban más időközt állítottam be (pl. 1 sec, 1,05 sec) akkor csak időnként kaptam hibás idő adatokat, hiszen csak időnként akadtak össze, akkor amikor pont egyszerre kérdezték le az RTC órát. A programot be sem idézem, elég tudni, hogy ez nem jó megoldás. Sajnos a dátum és az idő időnként még valós adatnak is tűnt, tehát nem lehetett kiszűrni a hibás adatokat a valós adatok közül. Két master egy buszon nem fér meg! Más megoldást kell keresni.

Táláltam egy halovány utalást az endTrasmission() függvény leírásában, ugyanis ennek lehet bemenő paramétere. Ha true értékkel hívjuk meg (ez az alapértelmezett) akkor felszabadítja a buszt más eszköz számára, false esetén pedig újabb kommunikációt kezdeményez, hogy ne vehesse el tőle senki a kommunikációt. Ez folyamatos adatátvitelnél lehet fontos. Sajnos ez az utalás kevés volt, első körben nem találtam infót, hogyan is kell ezt értelmezni és esetleg felhasználni a több master-es működéshez.

Miután hasztalanul keresgéltem, az maradt, ami mindig, hogy megcsinálom magam. Azt már tudtam, hogy egyszerre több master is lehet a buszon, de valahogyan biztosítani kell, hogy egyszerre ne akarjanak többen master-ek lenni. Eszembe jutott egy őskövület, amit az IBM fejlesztett ki több eszköz hálózaton történő párhuzamos működésére. Tokenring-nek hívták. A probléma ugyanaz. Többen is akarnak a hálózaton kommunikálni, de valahogyan el kell dönteni, hogy egy adott pillanatban ki az úr a vezetéken. Az eszközök sorban adogatják egymásnak körbe-körbe a státust, és akinél éppen a labda van, az beszélhet. Nincs kiemelt master szerep, és nem is lehet, hiszen a hálózatra tetszőleges számban felcsatlakozhatnak és el is tűnhetnek eszközök. Az IBM nem alkotott újat. Amerikában az indiánok ezt már sokkal régebben kitalálták. A törzsfőnökök ültek a tűz mellett, és mindig az beszélhetett, aki a kezében fogta a pipát. Nem is volt hangzavar.

Végül nem tokenringet csináltam, de majdnem. A működési elv a következő lett! Van egy fő-fő master (vezér master) a buszon. Ez felel meg az indián törzs varázslójának. Amit ez mond az van! Ha már senki nem tud megszólalni, ez akkor is… A fő master olyan kedves lény, hogy időnként átadja a master státust valaki másnak, és egy ideig türelmesen várakozik arra, hogy visszakapja a master jogot. Amíg várakozik, nem kommunikál az I2C buszon, így az ideiglenes master-rel biztosan nem zavarják egymást. Ha egy beállítható időn belül nem kapja vissza a master jogot, akkor úgy tekinti, hogy az ideiglenes master lefagyott, vagy nem működik, ezért újra saját kézbe veszi a dolgokat, akár egy reset-et is csinálhat a renitens Arduino-nak. Ehhez persze kell egy kimenet, amire 0-át állít be rövid időre. Ez a kimenet pedig az adott Arduino reset bemenetére lesz kötve. Ezzel egy watcdog áramkört is csináltunk. Szóval ha minden rendben van, akkor az ideiglenes master meg tudja szólítani az I2C buszon keresztül a fő masterünket, hiszen annak is van címe és éppen biztosan nem csinál semmit az I2C buszon! Így visszaadhatja a master státust. Amikor visszakapta fő masterünk a master státust, akkor keres egy másik slave-et, akinek odaadhatja az ideiglenes master státust, és újra vár, hogy visszakaphassa. Tehát fő master-ünkben lennie kell egy nyilvántartásnak, hogy milyen címen vannak slave-ek a buszon, akik időnként szeretnének a buszon master-ek lenni. Ez nálam egy tömb, aminek a tömb elemei címek, amiket sorban kiolvasok. A működés lehetne valódi tokenring is, hiszen a slave-ek adhatnák körben egymásnak a master státust. Csak azért döntöttem a fő master használata mellett, mert ha valami hiba miatt tokenring esetén megszakad a lánc, akkor a master státus „beragad” egy eszköznél. Fő masterünket természetesen watcdog funkcióval ruházzuk fel, így ha lefagy, akkor újraindul és a zenekar játszik tovább! Ha egy slave fagy le, akkor egy idő után a fő master visszaveszi a master státust, és még reset-et is küldhet, ha ezt akarjuk és van szabad kivezetésünk a funkcióra. Ha nincs szabad vezeték, vagy bízunk abban, hogy a slave sose fagy le, akkor ez nem kell! Fő master programjában slave-ek programjának ismeretében eldönthetjük, hogy melyik slave-nek mennyi időt adunk az I2C használatára, illetve más események alapján ütemezhetjük, hogy kinek mikor és mennyi időre adjuk meg a master jogokat.

Kedves olvasó! Ha ezek után azt hiszed, hogy ez egy nagyon bonyolult program, akkor nagyot tévedsz. Pár sor az egész. Van egy master és egy slave programforrás. Mindkettőben közös rész az RTC óra lekérdezéséhez szükséges függvények. Ennek semmi köze a multimaster megoldáshoz, csak a demó miatt raktam bele. Természetesen minden slave-nek kell egy I2C cím, és az összes slave címet fel kell sorolni a master programjában egy tömbben. A master címe konstans 10, így az összes slave a 10-es címen keresi a mastert, ha vissza akarja adni a master státust. Egyébként a slave-ek amikor master-ek, adatokat is tudnak küldeni egymásnak. Csak az első két byte nem lehet 0, mert ezt el fogja nyelni a multimaster algoritmus, hiszen két byte-ot mindenképpen elolvas az érkező adatból, és ha ez 00, akkor épp a master státust adogatjuk a buszon, ha meg nem 00, akkor adatok áramlanak az aktuális master-től egy kiválasztott slave felé, aki majd egyszer nem egy nem túl távoli jövőben (…és galaxisban) master is lesz még valamikor.

A programban sok a komment! Talán érthető belőle, hogy mi zajlik. A folyamat kipróbálásához két arduino-t használtam. Mindkettő soros porton csücsült és türelmesen figyeltem a soros monitort! A címeket tartalmazó tömb akármekkora lehet, a példa programban épp két slave címet is tartalmaz. Ezt mutatja be ezzel, hogy ha egy slave nincs a buszon, akkor a folyamat megy tovább, nem fagy le. Ez annak köszönhető, hogy a master státus átadása előtt egy beginTransmission() és endTransmission() párossal ellenőrzöm, hogy az adott számú slave jelen van-e a buszon! Ha jelen van, akkor az endTransmission() nullát ad vissza! Kipróbáltam, hogy a fő master kivételével bármit le lehet húzni a buszról, a többi eszköz boldogan működik tovább.

A forrásban a loop()-ban egy master_satus_allitas() függvényt kell egy if feltételében elhelyezni. Ez a lelke a fő master-nak. Ha az eszköz éppen master, akkor belemegy az if-be és itt kell az összes master I2C kommunikációt megcsinálni. A függvényhívásból visszajöhet azzal is, hogy már nem master, ekkor az if() nem fut le, de a loop() megy tovább. Ez a függvény az összes adminisztrációt elvégzi, nem kell másra figyelni a programban. Természetesen a loop()-ban nem lehet komolyabb idejű delay(), mert az megakasztja a folyamatot. A slave programjában is van egy if(). Ebben csak a master státust kérdezem le. Ha épp master státusban van, akkor belemegy az if()-be és csinálja amit az I2C buszon kell. Ha éppen slave, akkor meg nem megy bele, és mást csinál. Minden mást a wire megszakítás függvényei csinálnak. Ezek a függvények állítgatják a master_satus változót, amit a loop() ban is használunk, ezért ennek deklarálásakor használni kell a volatile jelzőt. Ezzel jelezzük a fordítónak, hogy a változót futás közben a ram-ban helyezze el, és ne módosítsa az értékükét a vezérlő belső regisztereiben. Ennek ugyanis az lehetne a következménye, hogy amikor az I2C buszon megérkezik egy adat, az generál egy megszakítást, meghívódik az előre definiált lekezelő függvény, ami esetleg más változó értékkel találja szemben magát, mint a loop()-ban párhuzamosan futó programrészek. Nem gondoltam bele, hogy ebben a programban ebből lehet-e probléma. A volatile nélkül is látszólag jól működött, de nem árt az óvatosság!

A forrás mindenféle lényegi változtatás nélkül felhasználható. Másolni kell setup előtti változó deklarációkat, a mastert_status_allitas() függvényt, és természetesen a wire által deklarált megszakítás függvényeket (jelen forrásban slave_fogad és slave_kuld néven), melyeket akkor hív meg a vezérlő, amikor adat érkezik, vagy adatot kell küldeni. Ezeket a függvényeket ki kell egészíteni saját programrészekkel, ha a slave-ek adatokat küldözgetnek egymásnak. Az említett korlátozás érvényben van, tehát az első két byte nem lehet 00, ha adatot küldünk. A 00 tartalom a master státus átadására és visszaadására szolgál.

Azt azért még el kell mondanom, hogy eddig csak kísérletezgettem a megoldással. Gyakorlatban még nem használtam, most folyik éppen a megoldás átépítése a jelenleg összevissza működő, lefagyogató, és hibás szélsebességet mutogató időjárás állomásomban, ami már beküldi az adatokat a 433Mhz-s adóval egy lakásban elhelyezett beltéri egységbe. Még hetek vannak hátra, de haladok!

A master programja:

#include<Wire.h> 
#include<avr/wdt.h>         //watchdog-ot fogunk használni ennek a z arduino-nak az újraindítására, ha netán lefagyna
#define CLOCK_ADDRESS 0x68  //a DS3231 óra IC címe az I2C buszon. 
long ido_tmp=millis();      //egy másodpercenként fogunk kiírni valamit, ennek időzítéséhez segédváltozó

struct dat_ido {      // a dátum és idő beállításához illetve kiolvasásához létrehozunk egy struktúrát dat_ido néven (változó típus)
  int ev;             //év tárolása a stuktúrában (évszázadokkal kell megadni, illetve így kapjuk meg az eredményt a dátum kiolvasásakor)
  byte ho;            //hó tárolása a struktúrában
  byte nap;           //nap tárolása a struktúrában
  byte het_napja;     //hét napja a struktúrában (1-hétfő....7-vasárnap)
  byte ora;           //óra tárolása a struktúrában (24 órás üzemmódot fogunk használni)
  byte perc;          //perc tárolása a struktúrában
  byte masodperc;     //másodperc tárolása a struktúrában
  bool pontos;        //ez a változó fogja megadni az IDO kiolvasását követően, hogy pontos-e a kiolvasott időpont 
                      //(ha elemes működés közben leáll a DS3231 oszcillátora, akkor az "OSF" flag bebillen, és ezt olvassuk ki
};

dat_ido ds3231;  //létrehozzuk az dat_ido típusú változónkat DS3231 változó néven

//i2c multimaster megoldáshoz:
//Ez a program a "valódi master", azaz jellemzően ez tartja magánál a master funkciókat és csak időnként adja át ezt a státust más slave-nek
//több változó is kellett a szükséges adminisztrációhoz. A loop()-ban  minden ciklusban lefut egy master_satus_allitas() függvény, ami méri 
//az eltelt időt, és ha letelt egy előre beállított idő, akkor átadja a master státust egy másik slave-nek. A státust azzal adja át, hogy
//küld az adott slave-nek 2db 0 tartalmú byte-ot, valamint adminisztrálja, hogy most nem saját maga a master, tehát nem végezhet olyan
//funkciókat, amiben az I2C buszon masterként viselkedne. Ezt legközelebb akkor teheti meg, ha visszakapja a master státust a kijelölt
//slave-től.  Ha egy beállítható időn belül nem kapja vissza a státust, akkor újra master-nek álltja be magát, és akár resetelheti is
//azt a slave-et, ami nem adta vissza a státust, tehát valószínűleg lefagyott. Ehhez azonban egy kivezetést el kell használni. 
//Több slave is lehet, aminek átadja a master státust, ezeket sorban szólítja meg. Ha valamelyik nincs éppen a buszon (nem válaszol egy
//Wire.beginTransmission();Wire.endTransmission(); párosra

volatile bool master_status=1;        //Ez a változó 1-el jelzi, ha nála van a master státus. Vagyis ilyenkor ez a program a "valódi master"!
                                      //Természetesen ez is egy slave, speciálisan a 10-es című az I2C buszon, de ez a slave általában
                                      //master funkcióban működik, csak időnként ezt a státust átadja más slave-eknek. Általában ennek a változónak
                                      //az értéke 1, csak akkor lesz 0, amikor épp átadta a master státust egy másik slave-nek. 
                                      //Normál esetben rövid időn belül visszakapja a master státust, addig ez 0, azaz nem szabad az I2C buszon
                                      //master funkciókat végrehajtania. Ha nem kapja vissza a master státust egy beállítható időn belül 
                                      //(master_vissza_ido), akkor visszaveszi magának. Ezt figyelembe kell venni a többi slave programjának
                                      //kialakításakor.
                                      //A loop()-ban a master_status_allitas() függvény vizsgálja ezt a változót, és ha ez 1-el tér vissza
                                      //akkor egy if()-ben ezt észreveszi a program, lefuttatja azokat a funkciókat amihez a master státus 
                                      //szükséges. Pl. kiolvassa az időt egy I2C RTC órából. 
                                      //A master_status_allitas() függvény fogja időnként átadni a master státust egy másik slave-nek, és ekkor
                                      //ez a váltoózó 0-ra lesz átállítva. Ilyenkor ez a program nem végezhet master funkciókat az I2C buszon!
                                      
const byte slave_cimek[]={11,12};     //azoknak slave-eknek a címe, amiknek szeretnénk időnként átadni a master státust. Tetszőleges mennyiségű
                                      //címet sorolhatunk fel, ha valamelyik nincs a buszon, az nem jelent problémát, mert észreveszi a program
                                      //és kihagyja az a slave-et a folyamatból. Ennek a slave-nek a saját I2C címe kötelezően 10, mert minden 
                                      //többi slave erre a címre küld státus visszaadáshoz jelzést, ha már nem kell neki a master státus.
                                      
const byte slave_reset_out[]={3,4};   //erre a kimenetre van rákötve az adott slave reset bemenete, erre kap egy 10msec-es low reset jelet, 
                                      //ha nem érkezik tőle nyugta a master státus átvételéről, ha ez 99, akkor nem kap reset jelet.
                                      //Ezzel egy watchdog funkciót lehet megvalósítani, de elhasznál egy kimenetet. Szinkronban kell
                                      //a tömböt beállítani a slave_cimek[] tömbbel.
                                      
const byte max_slave_idex=sizeof(slave_cimek) - 1;      //A rendszerben használható maximális slave cím index a tömbből történő kiválasztáshoz
byte akt_slave=max_slave_idex;                          //az aktuális slave sorszáma. akinél a mastar status van vagy ennél volt utoljára. 
                                                        //A slave_cimek[] és slave_reset_out[] tömböket címzi. Mivel először növeljük, 
                                                        //ezért az utolsó slave címére kell állítani, hogy a legelsővel kezdjük a master státus átadását
const long sajat_master_timeout=3000;                   //az az idő, ameddig magánál tarthatja a master státust, ha letelik, tovább kell adnia
long master_timeout_tmp=millis();                       //segédváltozó a master státus továbbadási idejének méréséhez
const long master_vissza_ido=10000;                     //ennyi ideig vár arra, hogy a következő master visszaadja a master státust. 
                                                        //Ha nem érkezik a mester státus ennyi idő múlva, akkor resetelheti azt a master, akinek átadta a master státust,
                                                        //vagy csak hibát jelezhet. Ebben a példa programban csak a soros porton ír ki egy hibaüzenetet.
long master_vissza_tmp=millis();                        //a master státus visszaszerzéséhez segédváltozó                                                         

void setup()
{
  Serial.begin(9600);             // soros port inicializálása            
  Wire.begin(10);                 // I2C kommunikáció inicializálása, ez is egy slave 10-es címmel
  Wire.onReceive(slave_fogad);    //Ezt a funkciót hívja az Arduino, amikor egy másik slave (aki éppen master-ként viselkedik) adatot küld 
  Wire.onRequest(slave_kuld);     //Ezt a funkciót hívja meg az Arduino, amikor egy másik slave (aki éppen master-ként viselkedik) adatot vár
  wdt_enable(WDTO_4S);            //engedélyezük a watcdog-ot 4 sekundum várakozással 4sec után a chip újraindítja magát, ha nincs közben
                                  // wdt_reset() függvényhívás, ami újraindítja a timert, ami megakadályozza a reset-et.
}

void loop()
{
  if (master_status_allitas()==1) {
    //itt olyan dolgokat csinál a program, amihez master státus kell
    if (ido_tmp+1000<millis()) {     //pl. időnként kiolvassuk az időt az órából
      ido_tmp=millis();
      getTime24();                   //kiolvassuk az időt a DS3132-ből a ds3231 változóba (dat_ido típusú változó)
      Serial.print(ds3231.ev,DEC);Serial.print('-');Serial.print(ds3231.ho,DEC);Serial.print('-');
      Serial.print(ds3231.nap,DEC);Serial.print("  ");Serial.print(ds3231.het_napja);
      Serial.print("  ");Serial.print(ds3231.ora,DEC);Serial.print(':');Serial.print(ds3231.perc,DEC);
      Serial.print(':');Serial.println(ds3231.masodperc,DEC);
    }
  }
  if (ido_tmp+1000<millis()) {  //egy másodpercenként futtatunk valamit
    wdt_reset();                //alaphelyzetbe állítjuk a a watcdog timer-t
                                //ha lefagyna ez a program, akkor 4sec múlva a chip reseteli magát
    Serial.println("Egyéb feladatok futnak...");
    ido_tmp=millis();
  }
} 


bool master_status_allitas() {
  if (master_status==1) {                                       //még mi vagyunk a master  
    if (master_timeout_tmp+sajat_master_timeout<millis()) {     //letelt az idő, átadjuk a master státust
      akt_slave++; if (akt_slave>max_slave_idex) {akt_slave=0;} //vesszük a következő slave címét a tömbből. Ha végére értünk a tömbnek akkor újra a 0. címet vesszük elő
      Wire.beginTransmission(slave_cimek[akt_slave]);           // elindít a master egy átvitelt a kiválasztott slave felé
      byte error = Wire.endTransmission();                      //kiolvassuk a hibakódot, ha ez 0 akkor nincs hiba a slave vonalon van és kommunikál
      if (error==0) {                                           //az adott slave a vonalon van és kommunikál, tehát átadjuk neki a master státust
        Wire.beginTransmission(slave_cimek[akt_slave]);         // elindít a master egy átvitelt a kiválasztott slave felé
        Wire.write(0);Wire.write(0);                            //ezzel adjuk át a master státust    
        Wire.endTransmission();                                 // vége az átvitelnek
        master_status=0;                                        //már nm mi vagyunk a master
        master_vissza_tmp=millis();                             //innen indul az időzítés a master státus visszaszerzéséhez
        Serial.print("Master státus átadnva:");
        Serial.print(slave_cimek[akt_slave]);
        Serial.println(" cimnek");
        return false;                                           //0-val térünk vissza, tehát már nem vagyunk master státusban
      }
      else {                                                    //nincs a vonalon a kiválasztott slave, tehát nem adjuk a master státust
        Serial.print("Nem tudta átadni:");
        Serial.print(slave_cimek[akt_slave]);
        Serial.println(" cimnek");
        master_timeout_tmp=millis();                            //innen indul az időzítés a következő master státus átadáshoz
      }
    }
  }
  else {                                                        //nem mi vagyunk a master, lehet ellenőrizni, hogy kell-e már visszavenni a master státust
    if (master_vissza_tmp+master_vissza_ido<millis()) {         //letelt a master státus visszaszerzési időzítés, reseteljük az aktuális slave-et, vagy hibát jelzünk
      Serial.println("Master visszaszerzés kényszerítése");
      if (slave_reset_out[akt_slave]!=99) {
        digitalWrite(slave_reset_out[akt_slave],LOW);
        delay(10);
        digitalWrite(slave_reset_out[akt_slave],HIGH);
        master_status=1;                //erőltetetten visszavesszük a master státust, ebből baj is lehet, ha nem volt reset
                                        //minden attól függ, hogy az a slave akitől most visszavesszük, hogy van megírva.
                                        //Ha pl az a slave úgy van megírva, hogy soha nem adja vissza a státust, csak elvégzi a feladatát
                                        //akkor ez így jó eljárás
        master_timeout_tmp=millis();    //innen indul az időzítés a következő master státus átadáshoz
        return true;                    //újra master státusban vagyunk
      }
    }
    return false;                       //nem vagyunk master státusban
  }
  return true;                          //ha eddig eljutunk, akkor épp master státusban vagyunk, és még nem telt le az idő, hogy átadjuk
}

void slave_fogad ()        
//Ez a függvény akkor indul, amikor a épp slave státusban van ez a program, és valaki akar valamit küldeni neki, 
//vagy valaki vissza akarja adni a master státust
{
  //2 byte-ot várunk a mastertől, és azt olvassuk be
  byte fogad1 = Wire.read();           
  byte fogad2 = Wire.read(); 
  if (fogad1==0 and  fogad2==0) {
    master_status=1;                                   //újra mi vagyunk a master
    master_timeout_tmp=millis();                       //elindul a master státus megtartásának időzítése
    Serial.println("Visszakapta a master státust...");
  }
  // ha nem egy slave adta vissza a master státustm akkor valakitől adatok jönnek, itt lehet fogadni a további adatokat, ha nem 0,0-val kezdődtek       
}

void slave_kuld()                            
//Ez a függvény akkor indul, amikor egy másik slave épp master státusban van és adatot kér tőlünk.
//Most épp nem használjuk!
{
}


void setTime24() {
/*********************************************************************************************************
* Ez egy saját DS3231 kezelő függvény, ami beállítja az chip-ben a dátumot és időt a ds3231 struktúrájú  *
* változó által meghatározott értékekre. Az évet évszázadokkal együtt várja (kivon belőle 2000-et.       *
* Az óra 24 órás üzemmódra áll, a négyszögjel kimenet 1Hz frekire áll be, és a 32khz-s kimenet tiltva.    *
*********************************************************************************************************/
  //status regiszter beállítása
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x0e);               //A control regiszterbe fogunk írni, azt címezzük meg
  Wire.write(0b01100011);         //BBSQW bit 1, vagyis elemes táplálásnál is működik az oszcillátor
                                  //ha 1-re állítanánk, elemes táplálásnál nem járna az óra, csak őrizni az utolsó időpontot
                                  //A RS2 és RS1 bit 0-val beállítja a négyszögjel kimenetet 1Hz-ra, ezen lehet változtatni:
                                  // 0b01101011 esetén 1024Khz, 0b01110011 esetén 4096Khz, 0b01111011 esetén 8192Khz.
                                  //A INTCN bit 0-val beállítja, hogy az INT/SQW kimenet beállított frekvenciájú jelet ad ki.
  //címbet a státus regiszter következik, nem kell megcímezni csak írni, és az adat oda kerül
  Wire.write(0b00000000);         //A status regiszterben töröljük az OSF fleg-et, ami 1-el jelzi majd, ha elemes táplálásnál 
                                  //nem működött az oszcillátor, vagyis az óra valószínűleg nem pontos.
                                  //Az EN32Khz bit-et is 0-ra írjuk, azaz letiltjuk a 32Khz-s kimenetet
  Wire.endTransmission();
  //dátum és időpont beállítása
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x00);
  Wire.write(dec_bcd(ds3231.masodperc));
  Wire.write(dec_bcd(ds3231.perc));
  Wire.write(dec_bcd(ds3231.ora) & 0b10111111);
  Wire.write(dec_bcd(ds3231.het_napja)); 
  Wire.write(dec_bcd(ds3231.nap));
  Wire.write(dec_bcd(ds3231.ho));
  Wire.write(dec_bcd(ds3231.ev-2000)); 
  Wire.endTransmission();
}

void getTime24() {
/*********************************************************************************************************
* Ez egy saját DS3231 kezelő függvény, ami kiolvassa chip regisztereiből a dátumot és az időt a ds3231   *
* struktúrájú változóba. Az évhez hozzáad 2000-et, vagyis ezt évszázadokkal adja vissza. Az időt 24 órás *
* formátumban adja vissza.                                                                               *
*********************************************************************************************************/
  //kiolvassa a dátumot és az időpontot 
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x00);
  Wire.endTransmission();
  Wire.requestFrom(CLOCK_ADDRESS, 7);
  ds3231.masodperc = bcd_Dec(Wire.read());
  ds3231.perc = bcd_Dec(Wire.read());
  ds3231.ora = bcd_Dec(Wire.read());
  ds3231.het_napja = bcd_Dec(Wire.read());
  ds3231.nap = bcd_Dec(Wire.read());
  ds3231.ho = bcd_Dec(Wire.read() & 0b01111111);
  ds3231.ev = bcd_Dec(Wire.read())+2000;
  //status regiszterből az OSF bit kiolvasása.
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x0f);
  Wire.endTransmission();
  Wire.requestFrom(CLOCK_ADDRESS, 1);
  if (Wire.read()& 0b10000000) {ds3231.pontos=0;} else {ds3231.pontos=1;}  //1-el jelezzük, ha az óra valószínűleg pontos
}

float getTemperature() {
                // Kiolvassa a hőmérséklet értékét a 0x11h és 0x12h regiszterekből
  byte temp;
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x11);         //a DS3231 chip 0x11 és 0x12-es regisztere a hőmérséklet regiszter
  Wire.endTransmission();
  Wire.requestFrom(CLOCK_ADDRESS, 2);         //2 byte-ot kérünk el a chip-től
  temp = Wire.read();                         //hőmérséklet MSB regisztere
  return float(temp) + 0.25*(Wire.read()>>6); //hőmérséklet LSB regiszterének kiolvasása
}

byte bcd_Dec(byte val) {  // Convertál bcd számból decimálisba
  return ( (val/16*10) + (val%16) );
}
byte dec_bcd(byte val) {  // Convertál decimális számból bcd-be
  return ( (val/10*16) + (val%10) );
}

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);
}

Egy futási eredményt is megmutatnék:

Nem tudta átadni:12 cimnek
2022-5-4  3  8:9:8
2022-5-4  3  8:9:9
2022-5-4  3  8:9:10
Master státus átadnva:11 cimnek
Egyéb feladatok fVisszakapta a master státust…
utnak…

Egyéb feladatok futnak…
2022-5-4  3  8:9:13
Nem tudta átadni:12 cimnek

Felhívnám a figyelmet a vastaggal kiemelt 6. sorra. Ebben látható hogy a master státus visszakapásakor a wire által megszakítással meghívott függvény megszakította a sorosportra írást. Jól működik, csak itt látható, hogy egyes esetekben milyen pontokon is megszakadhat a végrehajtás!

Csináltam egy kivonatot a forrásból! Nem működőképes program, de kivettem a kommentek nagy részét, és csak azokat a sorokat hagytam benne, amit másolni kell egy másik programba a működéshez:

volatile bool master_status=1;                 //ez a változó 1-el jelzi, ha nála van a master státus
const byte slave_cimek[]={11,12};              //azoknak slave-eknek a címe, amiknek szeretnénk időnként átadni a master státust. 
const byte slave_reset_out[]={3,4};            //erre a kimenetre van rákötve az adott slave reset bemenete, ha 99, akkor nincs reset jel
const byte max_slave_idex=sizeof(slave_cimek) - 1;      //a rendszerben használható maximális slave cím index a tömbből történő kiválasztáshoz
byte akt_slave=max_slave_idex;                          //az aktuális slave sorszáma. akinél a mastar status van vagy ennél volt utoljára. 
const long sajat_master_timeout=3000;                   //az az idő, ameddig magánál tarthatja a master státust, ha letelik, tovább kell adnia
long master_timeout_tmp=millis();                       //segédváltozó a master státus továbbadási idejének méréséhez
const long master_vissza_ido=10000;                     //ennyi ideig vár arra, hogy a következő master visszaadja a master státust. 
long master_vissza_tmp=millis();                        //a master státus visszaszerzéséhez segédváltozó                                                         

void setup()
{
  Wire.begin(10);               // I2C kommunikáció inicializálása, ez is egy slave 10-es címmel
  Wire.onReceive(slave_fogad);  //Ezt a funkciót hívja az Arduino, amikor egy másik slave (aki éppen master-ként viselkedik) adatot küld 
  Wire.onRequest(slave_kuld);   //Ezt a funkciót hívja meg az Arduino, amikor egy másik slave (aki éppen master-ként viselkedik) adatot vár
}

void loop()
{
  if (master_status_allitas()==1) {
    //itt olyan dolgokat csinál a program, amihez master státus kell
  }
  //további feladatok, amihez nem kell master státus} 


bool master_status_allitas() {
  //ha épp itt van a master státus és letelt a kötelező átadási idő, akkor átadjuk a mastar státust a soron következő szereplőnek
  if (master_status==1) {                                       //még mi vagyunk a master  
    if (master_timeout_tmp+sajat_master_timeout<millis()) {     //letelt az idő, átadjuk a master státust
      akt_slave++; if (akt_slave>max_slave_idex) {akt_slave=0;} //vesszük a következő slave címét a tömbből. Ha végére értünk a tömbnek akkor újra a 0. címet vesszük elő
      Wire.beginTransmission(slave_cimek[akt_slave]);           // elindít a master egy átvitelt a kiválasztott slave felé
      byte error = Wire.endTransmission();                      //kiolvassuk a hibakódot, ha ez 0 akkor nincs hiba a slave vonalon van és kommunikál
      if (error==0) {                                           //az adott slave a vonalon van és kommunikál, tehát átadjuk neki a master státust
        Wire.beginTransmission(slave_cimek[akt_slave]);         // elindít a master egy átvitelt a kiválasztott slave felé
        Wire.write(0);Wire.write(0);                            //ezzel adjuk át a master státust    
        Wire.endTransmission();                                 // vége az átvitelnek
        master_status=0;                                        //már nm mi vagyunk a master
        master_vissza_tmp=millis();                             //innen indul az időzítés a master státus visszaszerzéséhez
        return false;                                           //0-val térünk vissza, tehát már nem vagyunk master státusban
      }
      else {                                                    //nincs a vonalon a kiválasztott slave, tehát nem adjuk a master státust
        master_timeout_tmp=millis();                            //innen indul az időzítés a következő master státus átadáshoz
      }
    }
  }
  else {                                                        //nem mi vagyunk a master, lehet ellenőrizni, hogy kell-e már visszavenni a master státust
    if (master_vissza_tmp+master_vissza_ido<millis()) {         //letelt a master státus visszaszerzési időzítés, reseteljük az aktuális slave-et, vagy hibát jelzünk
      if (slave_reset_out[akt_slave]!=99) {
        digitalWrite(slave_reset_out[akt_slave],LOW);
        delay(10);
        digitalWrite(slave_reset_out[akt_slave],HIGH);
        master_status=1;                //erőltetetten visszavesszük a master státust, ebből baj is lehet, ha nem volt reset
                                        //minden attól függ, hogy az a slave akitől most visszavesszük, hogy van megírva.
                                        //Ha pl az a slave úgy van megírva, hogy soha nem adja vissza a státust, csak elvégzi a feladatát
                                        //akkor ez így jó eljárás
        master_timeout_tmp=millis();    //innen indul az időzítés a következő master státus átadáshoz
        return true;                    //újra master státusban vagyunk
      }
    }
    return false;                       //nem vagyunk master státusban
  }
  return true;                          //ha eddig eljutunk, akkor épp master státusban vagyunk, és még nem telt le az idő, hogy átadjuk
}

void slave_fogad ()        
//Ez a függvény akkor indul, amikor a épp slave státusban van ez a program, és valaki akar valamit küldeni neki, 
//vagy valaki vissza akarja adni a master státust
{
  //2 byte-ot várunk a mastertől, és azt olvassuk be
  byte fogad1 = Wire.read();           
  byte fogad2 = Wire.read(); 
  if (fogad1==0 and  fogad2==0) {
    master_status=1;                                   //újra mi vagyunk a master
    master_timeout_tmp=millis();                       //elindul a master státus megtartásának időzítése
  }
  // ha nem egy slave adta vissza a master státustm akkor valakitől adatok jönnek, itt lehet fogadni a további adatokat, ha nem 0,0-val kezdődtek       
}

void slave_kuld()                            
//Ez a függvény akkor indul, amikor egy másik slave épp master státusban van és adatot kér tőlünk.
//Most épp nem használjuk!
{
}

A slave-ek programja. Minden slave-nek legyen más a címe, azt a setup() ban a wire.begin()-ben kell megadni, valamint a mester slave_cimek[] tömbben is fel kell sorolni.

#include<Wire.h> 
#define CLOCK_ADDRESS 0x68  //a DS3231 óra IC címe az I2C buszon. 
long ido_tmp=millis();      //egy másodpercenként fogunk kiírni valamit, ennek időzítéséhez segédváltozó

struct dat_ido {      // a dátum és idő beállításához illetve kiolvasásához létrehozunk egy struktúrát dat_ido néven (változó típus)
  int ev;             //év tárolása a struktúrában (évszázadokkal kell megadni, illetve így kapjuk meg az eredményt a dátum kiolvasásakor)
  byte ho;            //ho tárolása a struktúrában
  byte nap;           //nap tárolása a struktúrában
  byte het_napja;     //hét napja a struktúrában (1-hétfő....7-vasárnap)
  byte ora;           //óra tárolása a struktúrában (24 órás üzemmódot fogunk használni)
  byte perc;          //perc tárolása a struktúrában
  byte masodperc;     //másodperc tárolása a struktúrában
  bool pontos;        //ez a változó fogja megadni az IDO kiolvasását követően, hogy pontos-e a kiolvasott időpont 
                      //(ha elemes működés közben leáll a DS3231 oszcillátora, akkor az "OSF" flag bebillen, és ezt olvassuk ki
};

dat_ido ds3231;  //létrehozzuk az dat_ido típusú változónkat DS3231 változó néven

//i2c multimaster megoldáshoz
volatile bool master_status=1;        //ez a változó 1-el jelzi, ha nála van a master státus. Ekkor a loop()-ban 
                             //egy if()-ben ezt észreveszi a program, lefuttatja azokat a funkciókat amihez a
                             //a master státus szükséges. Pl. kiolvassa az időt egy I2C RTC órából. Amint ezekkel
                             //végzett, visszaadja a master státust a valódi masternek (10-es I2C cím) azzal, hogy
                             //küld 2db 0 értékű byte-ot neki. Ugyanekkor ezt a változót 0-ra állítja, így a következő
                             //loop()-ban már az if() kihagyja azokat a funkciókat amihez master státus szükséges.
byte error;                  //Mielőtt a valódi master-nek küldi a 2db 0 byte-ot, megszólítja Wire.beginTransmission(10)-el
                             //és az ezt követő Wire.endTransmission() ad vissza hibakódot. Ha ez 0, akkor a valódi master
                             //jelen van. Ha nem 0. akkor nem elérhető. Ekkor a master státust nem tudjuk visszaadni, de feltételezzük
                             //hogy rövidesen visszaveszi magának, tehát nekünk sem szabad master funkciókat futtatni az I2C buszon.

void setup()
{
  Serial.begin(9600);           // soros port inicializálása            
  Wire.begin(11);               // I2C kommunikáció inicializálása, ez lesz a 11-es című slave
  Wire.onReceive(slave_fogad);  //Ezt a funkciót hívja az Arduino, amikor az aktuális master adatot küld
  Wire.onRequest(slave_kuld);   //Ezt a funkciót hívja meg az Arduino, amikor az aktuális master kér
}

void loop()
{
  if (master_status==1) {       //a valódi master engedélyezte 2db 0 tartalmú byte küldésével, hogy master legyen az I2C buszon 
                                //itt olyan dolgokat csinál a program, amihez master státus kell, pl. kiolvassa az időt
                                //egy RTC órából
    getTime24();                //kiolvassuk az időt a DS3132-ből a ds3231 változóba (dat_ido típusú változó)
    Serial.print(ds3231.ev,DEC);Serial.print('-');Serial.print(ds3231.ho,DEC);Serial.print('-');
    Serial.print(ds3231.nap,DEC);Serial.print("  ");Serial.print(ds3231.het_napja);
    Serial.print("  ");Serial.print(ds3231.ora,DEC);Serial.print(':');Serial.print(ds3231.perc,DEC);
    Serial.print(':');Serial.println(ds3231.masodperc,DEC);
    //vége azon funkciók végrehajtásának amihez master státus kell, jelzi a valódi master-nek, hogy vége a master tevékenységnek
    Wire.beginTransmission(10);             // megszólítja a 10-es címet, hogy ott van-e a valódi master
    error = Wire.endTransmission();         //ezzel ellenőrizzük, hogy a valódi master, aki most éppen a 10-es című slave, a vonalon van-e
    if (error==0) {                         //a valódi master (éppen most slave) az I2C buszon van és kommunikál, tehát visszaadjuk neki a master státust
      Wire.beginTransmission(10);           //elindít egy átvitelt a 10-es címre, hogy visszaadja a master státust
      Wire.write(0);Wire.write(0);          //ezzel adjuk át a master státust    
      Wire.endTransmission();               //vége az átvitelnek
    }
    master_status=0;                        //a master funkciók nem futnak, amíg újra nem kap rá engedélyt a valódi master-től, ha nem tudta
                                            //jelezni a valódi master-nek, akkor is így tesz, mert nem tudni, hogy miért nem volt elérhető
                                            //a valódi master, és egy idő után a valódi master mindenképpen visszaveszi a master státust
                                            //és kommunikálni fog az I2C buszon, és összeakadhatnának.
  }
  //itt következnek azok a feladatok, amiket egyébként végrehajt a program, ezekben nem lehet olyan, 
  //ami az I2C buszon master kommunikációt igényel.
  if (ido_tmp+1000<millis()) {
    Serial.println("Egyéb feladatok futnak...");
    ido_tmp=millis();
  }
} 


void slave_fogad ()        
//Ez a függvény akkor indul, amikor a valódi master (vagy bári más, aki éppen master státusban van)
//küldeni akar adatokat ennek a slave-nek, aminek a címe most éppen 11. A küldött adatok nem kezdődhetnek
//0,0-val, mert ez azt jelzi, hogy master státust kapott ez a slave, és kommunikálhat az I2C buszon master-ként.
{
  //2 byte-ot várunk a master-től, és azt olvassuk be
  byte fogad1 = Wire.read();           
  byte fogad2 = Wire.read(); 
  if (fogad1==0 and  fogad2==0) {  //ha 2db 0-át kaptunk, akkor master státust kaptunk
    master_status=1;             //Beállítjuk a master_status változót 1-re, ezzel a loop-ban engedélyezzük 
                                 //azoknak a funkcióknak a végrehajtását, amivel az I2C buszon master-ként tevékenykedhet a program
    Serial.println("Master lett...");
  }
  //ha nem a master státust kaptuk meg, vagyis nem 2db 0 byte érkzezett, akkor itt
  //további adatokat lehet fogadni. Nem szabad elfelejteni, hogy az első két byte-ot 
  //"elnyeli" a program, de itt értékes adatokat lehet még fogadni a ha szükséges       
}

void slave_kuld()                            
//Ez a függvény akkor indul, amikor egy másik master adatot kér a slave-től, most épp nem használjuk
{
}


void setTime24() {
/*********************************************************************************************************
* Ez egy saját DS3231 kezelő függvény, ami beállítja az chip-ben a dátumot és időt a ds3231 struktúrájú  *
* változó által meghatározott értékekre. Az évet évszázadokkal együtt várja (kivon belőle 2000-et.       *
* Az óra 24 órás üzemmódra áll, a négyszögjel kimenet 1Hz frekireáll be, és a 32khz-s kimenet tiltva.    *
*********************************************************************************************************/
  //status regiszter beállítása
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x0e);               //A control regiszterbe fogunk írni, azt címezzük meg
  Wire.write(0b01100011);         //BBSQW bit 1, vagyis elemes táplálásnál is működik az oszcillátor
                                  //ha 1-re állítanánk, elemes táplálásnál nem járna az óra, csak őrizni az utolsó időpontot
                                  //A RS2 és RS1 bit 0-val beállítja a négyszögjel kimenetet 1Hz-ra, ezen lehet változtatni:
                                  // 0b01101011 esetén 1024Khz, 0b01110011 esetén 4096Khz, 0b01111011 esetén 8192Khz.
                                  //A INTCN bit 0-val beállítja, hogy az INT/SQW kimenet beállított frekvenciájú jelet ad ki.
  //címbet a státus regiszter következik, nem kell megcímezni csak írni, és az adat oda kerül
  Wire.write(0b00000000);         //A status regiszterben töröljük az OSF fleg-et, ami 1-el jelzi majd, ha elemes táplálásnál 
                                  //nem működött az oszcillátor, vagyis az óra valószínűleg nem pontos.
                                  //Az EN32Khz bit-et is 0-ra írjuk, azaz letiltjuk a 32Khz-s kimenetet
  Wire.endTransmission();
  //dátum és időpont beállítása
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x00);
  Wire.write(dec_bcd(ds3231.masodperc));
  Wire.write(dec_bcd(ds3231.perc));
  Wire.write(dec_bcd(ds3231.ora) & 0b10111111);
  Wire.write(dec_bcd(ds3231.het_napja)); 
  Wire.write(dec_bcd(ds3231.nap));
  Wire.write(dec_bcd(ds3231.ho));
  Wire.write(dec_bcd(ds3231.ev-2000)); 
  Wire.endTransmission();
}

void getTime24() {
/*********************************************************************************************************
* Ez egy saját DS3231 kezelő függvény, ami kiolvassa chip regisztereiből a dátumot és az időt a ds3231   *
* struktúrájú változóba. Az évhez hozzáad 2000-et, vagyis ezt évszázadokkal adja vissza. Az időt 24 órás *
* formátumban adja vissza.                                                                               *
*********************************************************************************************************/
  //kiolvassa a dátumot és az időpontot 
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x00);
  Wire.endTransmission();
  Wire.requestFrom(CLOCK_ADDRESS, 7);
  ds3231.masodperc = bcd_Dec(Wire.read());
  ds3231.perc = bcd_Dec(Wire.read());
  ds3231.ora = bcd_Dec(Wire.read());
  ds3231.het_napja = bcd_Dec(Wire.read());
  ds3231.nap = bcd_Dec(Wire.read());
  ds3231.ho = bcd_Dec(Wire.read() & 0b01111111);
  ds3231.ev = bcd_Dec(Wire.read())+2000;
  //status regiszterből az OSF bit kiolvasása.
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x0f);
  Wire.endTransmission();
  Wire.requestFrom(CLOCK_ADDRESS, 1);
  if (Wire.read()& 0b10000000) {ds3231.pontos=0;} else {ds3231.pontos=1;}  //1-el jelezzük, ha az óra valószínűleg pontos
}

float getTemperature() {
  // Kiolvassa a hőmérséklet értékét a DS3231 chip 0x11h és 0x12h regiszterekből
  byte temp;
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x11);                           //a DS3231 chip 0x11 és 0x12-es regisztere a hőmérséklet regiszter
  Wire.endTransmission();
  Wire.requestFrom(CLOCK_ADDRESS, 2);         //2 byte-ot kérünk el a chip-től
  temp = Wire.read();                         //hőmérséklet MSB regisztere
  return float(temp) + 0.25*(Wire.read()>>6); //hőmérséklet LSB regiszterének kiolvasása
}

byte bcd_Dec(byte val) {  // Convertál bcd számból decimálisba
  return ( (val/16*10) + (val%16) );
}
byte dec_bcd(byte val) {  // Convertál decimális számból bcd-be
  return ( (val/10*16) + (val%10) );
}

Egy futási eredményt lássunk arról is, mi zajlik a slave-ben:

Egyéb feladatok futnak…
Egyéb feladatok futnak…
Egyéb feladatok futnak…
Master lett…
2022-5-4  3  9:59:26
Egyéb feladatok futnak…
Egyéb feladatok futnak…
Egyéb feladatok futnak…
Egyéb feladatok futnak…
Egyéb feladatok futnak…
Egyéb feladatok futnak…
Master lett…
2022-5-4  3  9:59:32
Egyéb feladatok futnak…
Egyéb feladatok futnak…

Csináltam egy kivonatot a fenti programból is. Kivettem a kommentek nagy részét, és csak azokat a sorokat hagytam benne, amit másolni kell egy másik programba a működéshez:

volitale bool master_status=1;        //ez a változó 1-el jelzi, ha nála van a master státus. 
byte error;                           //segédváltozó annak ellenőrzéséhez, hogy a fő master a vonalon van-e

void setup()
{
  Wire.begin(11);               // I2C kommunikáció inicializálása, ez lesz a 11-es című slave
  Wire.onReceive(slave_fogad);  //Ezt a funkciót hívja az Arduino, amikor az aktuális master adatot küld
  Wire.onRequest(slave_kuld);   //Ezt a funkciót hívja meg az Arduino, amikor az aktuális master kér
}

void loop()
{
  if (master_status==1) {       //Megkapta a master státust, lehet használni az I2C buszt
                                //itt olyan dolgokat csinál a program, amihez master státus kell
    Wire.beginTransmission(10);             // megszólítja a 10-es címet, hogy ott van-e a valódi master
    error = Wire.endTransmission();         //ezzel ellenőrizzük, hogy a valódi master, aki most éppen a 10-es című slave, a vonalon van-e
    if (error==0) {                         //a valódi master (éppen most slave) az I2C buszon van és kommunikál, tehát visszaadjuk neki a master státust
      Wire.beginTransmission(10);           //elindít egy átvitelt a 10-es címre, hogy visszaadja a master státust
      Wire.write(0);Wire.write(0);          //ezzel adjuk át a master státust    
      Wire.endTransmission();               //vége az átvitelnek
    }
    master_status=0;                        //a master funkciók nem futnak, amíg újra nem kap rá engedélyt 
  }
  //itt következnek azok a feladatok, amiket egyébként végrehajt a program, ezekben nem lehet olyan, 
  //ami az I2C buszon master kommunikációt igényel.
} 


void slave_fogad ()        
//Ez a függvény akkor indul, amikor a valódi master (vagy bári más, aki éppen master státusban van)
//küldeni akar adatokat ennek a slave-nek, aminek a címe most éppen 11. A küldött adatok nem kezdődhetnek
//0,0-val, mert ez azt jelzi, hogy master státust kapott ez a slave, és kommunikálhat az I2C buszon master-ként.
{
  //2 byte-ot várunk a master-től, és azt olvassuk be
  byte fogad1 = Wire.read();           
  byte fogad2 = Wire.read(); 
  if (fogad1==0 and  fogad2==0) {  //ha 2db 0-át kaptunk, akkor master státust kaptunk
    master_status=1;             //Beállítjuk a master_status változót 1-re, ezzel a loop-ban engedélyezzük 
                                 //azoknak a funkcióknak a végrehajtását, amivel az I2C buszon master-ként tevékenykedhet a program
  }
  //ha nem a master státust kaptuk meg, vagyis nem 2db 0 byte érkzezett, akkor itt
  //további adatokat lehet fogadni. Nem szabad elfelejteni, hogy az első két byte-ot 
  //"elnyeli" a program, de itt értékes adatokat lehet még fogadni a ha szükséges       
}

void slave_kuld()                            
//Ez a függvény akkor indul, amikor egy másik master adatot kér a slave-től, most épp nem használjuk
{
}

Mennyire volt hasznos amit olvastál? Értékelés után szövegesen is leírhatod megjegyzéseidet és véleményedet!

Kattints egy csillagra az értékeléshez!

Szövegesen is leírhatod véleményedet! Ha kérdésed van, ne felejtsd el megadni az email címedet!