Opties voor communicatie tussen Arduinos

Ik ben op zoek om te communiceren tussen ~ 5-10 Arduinos om opdrachten en gegevens tussen hen te verzenden. Met "Arduino" bedoel ik de ATmega328 geprogrammeerd met behulp van het Arduino-programmeerraamwerk.

Elk van de apparaten zou op dezelfde PCB staan.

Ik weet dat I2C en SPI tot op zekere hoogte kunnen communiceren, maar dat ze echt bedoeld zijn voor randapparaten die op een microcontroller zijn aangesloten in plaats van meerdere microcontrollers met elkaar te verbinden en dat ze meestal maar een paar bytes aan gegevens tegelijk verzenden . Aan de andere kant weet ik dat LCD-schermen kunnen worden bestuurd met behulp van deze, en ik kan me voorstellen dat er in die gevallen veel gegevens worden overgedragen.

Bestaat er een I2C-verbinding (multi-master, multi-slave, single-ended, seriële computerbus) voor communicatie tussen microcontrollers? Toestaan ​​dat elk apparaat ongevraagd gegevens naar elkaar verzendt. Als bijvoorbeeld een schakelaar op apparaat A werd ingedrukt, zou dit apparaat B vertellen, zonder dat apparaat B altijd A zou vragen of er op een schakelaar was gedrukt. en vice versa.

Edit: I found some libraries that may do what I want with RS-485. Is this the only viable option for ~1k bps transmission between 5-10 devices? I would imagine TCP/IP over Ethernet would work as well, but that seems like too much. Are there any I2C libraries that allow sending that amount of data between only master devices?

5
bedoel je RS-485?
toegevoegd de auteur user7461, de bron
@Paul Ik wil een scheiding van zorgen hebben. Ik weet dat ik gewoon een grotere controller kan krijgen met meer onderbrekingen en I/O, maar ik houd de dingen liever simpel op apparaatniveau. Het helpt ook bij het samenwerken met anderen, omdat elk subsysteem onafhankelijk kan worden ontworpen en zelfs onafhankelijk kan worden geconstrueerd en later met elkaar kan worden verbonden. Het kan ook eenvoudiger zijn om de test te testen en te vervangen/upgraden.
toegevoegd de auteur user7461, de bron
@ frarugi87 Ik denk dat een bustopologie het beste werkt, waarbij elk apparaat communiceert met een ander apparaat en de verbinding tot stand brengt. Dus geen slaven.
toegevoegd de auteur user7461, de bron
Je hebt een fysieke laag nodig (bekabeling of bord), elektronische "standaard" zoals RS-485 maar 5V TTL is ook goed. Vervolgens moet u kiezen welk type busarbitrage (round-robin, master-slave of broadcast-berichten) en waarschijnlijk enige adressering of foutcontrole uitvoeren.
toegevoegd de auteur dotnetengineer, de bron
Op een breadboard zou ik geen RS485 gebruiken, omdat het transceivers nodig heeft. Slechts 5V voor een logica 1 en 0V voor een logische 0. Maar vanaf dat moment zijn de mogelijkheden eindeloos. Het hangt ook af van de snelheidseisen etc.
toegevoegd de auteur dotnetengineer, de bron
Je hebt niet aangegeven waarom je zoiets wilt creëren. Als je een tekort hebt aan IO-pinnen, zou je IO-expanders kunnen gebruiken. Er kunnen betere oplossingen zijn. Als het een hobby/proof of concept is, kun je andere projecten zoals deze bekijken en proberen het te verbeteren/aanpassen.
toegevoegd de auteur dotnetengineer, de bron
Heb je gekeken naar de seriële verbinding?
toegevoegd de auteur user67244, de bron
Er is een zeer vergelijkbare vraag hier: Hoe meerdere Arduino's met een Rpi te verbinden om home-lights/switches te besturen - het Rpi-deel negerend (het was niet echt relevant voor de vraag) Ik tel dat als vrijwel identiek aan wat je wilt do. Ik heb daar een lang antwoord gegeven over het doen van een "rolling master" -systeem. Lees de andere vraag/antwoord en kijk wat je ervan vindt.
toegevoegd de auteur Nick Gammon, de bron
een andere micro moet je n hop maken, maar het werkt. De laatste oplossing is wat je zei, dus iedereen spreekt in dezelfde bus, maar je hebt botsingsdetectie nodig. In dit geval stel ik u de CAN-bus voor, omdat deze de botsingen al verwerkt (maar u moet deze in uw code implementeren).
toegevoegd de auteur Tom Collins, de bron
Nou, oké voor bustopologie, maar ik raad je ten zeerste aan om één busmaster en alle andere slaven te maken. Anders heb je botsingen en is het detecteren/voorkomen van botsingen een "moeilijke" taak. Als je maar één meester hebt, kun je dit vermijden, omdat alleen hij toestemming kan geven om te praten. U kunt een seriële (UART) -bus maken met één draad die de TX van de master verbindt met elke slave RX en een draad die slaves TX (misschien met een buffer met 3 toestanden) met de master RX verbindt. Anders kunt u een daisy-chain maken: TX1 verbonden met RX2, TX2 naar RX3 ... TXn naar RX1. Dit is een beetje minder efficiënt, omdat om mee te praten
toegevoegd de auteur Tom Collins, de bron
Misschien heb ik het niet alleen gevonden, maar ik heb de fundamentele vraag niet gelezen: "Wat is de topologie die je nodig hebt"? Heb je iedereen nodig die met iedereen praat? Gecentraliseerde toegang (dat wil zeggen iedereen die met één spreekt)? Een gemengde oplossing?
toegevoegd de auteur Tom Collins, de bron

4 antwoord

Er zijn een aantal concepten die je kunnen helpen je droom te realiseren. Ik kan niet in de ruimte van dit antwoord precies zeggen hoe je moet implementeren wat je wilt, maar ik kan je de concepten laten zien die je zullen helpen om het te implementeren.

Allereerst is er het concept van de busmaster . Dit is niet noodzakelijkerwijs het apparaat dat communicatie tot stand brengt - in plaats daarvan is dit het apparaat dat de eigenaar is van en de bus bedient.

Wanneer een apparaat dat niet de busmaster is, op de bus wil communiceren, vraagt ​​het eerst toestemming aan de busmaster. De oude Z80 (nou ja, ik zeg "oud", maar ze zijn tegenwoordig nog in veel vormen) gebruikte dit concept om andere chips toe te staan ​​om de data- en adresbussen te gebruiken. Het bestaat uit twee signalen - BUSRQ en BUSACK. Een apparaat kijkt eerst of BUSRQ of BUSACK actief zijn en of geen van beide BUSRQ activeert. Als de busmaster bereid is om de bus op te geven aan het andere apparaat (hij gebruikt het op dat moment niet), activeert het BUSACK en weet het andere apparaat dat het de bus kan gebruiken. Niets anders kan het gebruiken totdat BUSRQ en BUSACK beide zijn vrijgegeven. Mooi en eenvoudig en elegant.

Maar niet perfect. Als twee apparaten beide besluiten om op hetzelfde moment om de bus te vragen, krijg je een botsing . Dit is een veel voorkomend probleem bij gedeelde bussystemen zoals dit, en veroorzaakt onnoemelijke problemen, tenzij je weet hoe je het op de juiste manier moet gebruiken.

Voer het concept luister-terwijl-je-praatje in. Dit betekent dat het apparaat dat de bus verzendt ook luistert naar wat er op de bus wordt verzonden via een afzonderlijke ontvanger. Het kan dan weten of wat het in de bus heeft verzonden, is wat er feitelijk in de bus is terechtgekomen. Als twee apparaten bijvoorbeeld op hetzelfde moment praten en één 10011001 verzendt en de andere 11001100 verzendt, kan het resultaat dat op de bus verschijnt, uiteindelijk iets anders zijn, zoals 11011101 of misschien 10001000 , afhankelijk van hoe de bussignalen zijn gemaakt. Dus als je weet wat je hebt verzonden, kan je er nu iets aan doen.

Volgend concept: back-off . Dit is waar beide afzenders een korte periode wachten en proberen opnieuw te verzenden. Zolang ze beiden een bepaalde tijd uitstellen, zal de eerste die probeert de bus pakken en kunnen communiceren. Maar hoe garandeer je dat ze beide voor verschillende tijden uitgesteld zullen worden? U denkt misschien dat het antwoord eenvoudig is: gebruik een willekeurig getal, zoals rand() of random() . Maar dat is ook een probleem:

Another concept: The pseudo random number generator

De Arduino genereert geen willekeurige getallen. Het gebruikt gewoon een complexe wiskundige formule om een ​​reeks getallen te creëren die voor ons willekeurig kijken . Dat zijn ze echter niet. Schrijf een klein programma om 10 willekeurige nummers serieel af te drukken en meerdere keren uit te voeren (druk op de resetknop). U zult steeds dezelfde "willekeurige" nummers in dezelfde volgorde vinden. Probeer het op een andere Arduino en je krijgt dezelfde nummers opnieuw. Altijd hetzelfde.

Dus wat te doen? Het antwoord heet seeding de generator van willekeurige getallen. Het volgende nummer gegenereerd door rand() et al hangt af van het nummer dat als laatste is gegenereerd. Dus verander het eerste nummer en de rest van de nummers zal veranderen. Je hebt echter een catch-22-situatie. Je hebt een willekeurig nummer nodig om de generator van willekeurige getallen in te zaaien om het willekeurig te maken om een ​​willekeurig getal te kunnen genereren om de toevalsgetalgenerator ... ad infinitum in te zaaien. Zie je waar dat heengaat? Je kunt niet seeden van rand() omdat rand() niet willekeurig is totdat je uit een willekeurige bron hebt gezaaid. Dus je moet een willekeurige bron vinden.

En dat is geen gemakkelijke taak. De beste bron van entropy zoals die bekend is, is witte ruis . Dit kan op een aantal manieren worden gegenereerd met een aantal verschillende circuits - van de afbraak van een diodeovergang tot een zeer hoge versterkingsversterking van de thermische fluctuaties in een weerstand.

Ze zijn allemaal behoorlijk ingewikkeld voor wat je echt wilt, maar er is een eenvoudigere, zij het iets minder willekeurige methode: lees een analoge ingang die nergens mee verbonden is. Het zal niet zoveel bereik hebben als een geschikte entropie-generator, maar het moet voldoende willekeur bieden om een ​​redelijke kans te geven dat elk apparaat een ander zaad krijgt.

Een ander nuttig concept is de interrupt.

Dit is goed in een situatie waarin je niet de complexiteit van een multi-masterbus met alle botsingen wilt, enz. Je hebt één master die al het werk op de bus doet en wanneer een slave-apparaat iets belangrijks te zeggen heeft het duwt de meester met een interrupt. De meester gaat dan "Ja? Wat wil je?" waarop de slaaf antwoordt: "Iemand heeft op mijn knop gedrukt".

Op die manier pompt de meester niet constant de slaaf om te zien of de knop is ingedrukt. Het wordt vaak gebruikt in busarrangementen zoals SPI en er zijn veel chips, zoals IO-expanderchips, die een onderbreking kunnen doen gelden wanneer een van hun ingangspinnen van status verandert.

Maar als u 20 apparaten heeft, betekent dit dat u 20 interrupt-pinnen hebt? Niet noodzakelijk. Nieuw concept: bedraad OF .

Het is perfect mogelijk om meerdere verschillende slaves allemaal met dezelfde interrupt-pin te gebruiken. De pin wordt normaal HOOG gehouden met een weerstand (dit kan een interne pullup-weerstand zijn) en elke slave heeft een open drain -uitgang die op die pin is aangesloten. Een open afvoeruitgang, wanneer "uit", is nergens op aangesloten - het is alsof de pin in de invoermodus is (in feite kan deze worden geëmuleerd op chips die geen open afvoer hebben door te schakelen tussen de invoer- en uitvoermodus). Wanneer de uitgang "aan" staat, verbindt deze de pin met de aarde en trekt de IO-pin naar beneden, net als een knop.

Het is dan aan de meester om zijn weg te banen rond de slaven waarvan het weet dat ze gehecht zijn aan die interrupt om te zien wie aandacht nodig heeft. Je kunt natuurlijk een aantal verschillende interrupt-pinnen implementeren met verschillende groepen slaves op elke - misschien een hoge prioriteit met slechts één apparaat, en lagere prioriteit degenen met elk een, bijvoorbeeld, meerdere apparaten.

Hetzelfde concept van bedrade OF en open afvoer kan worden gebruikt om meerdere apparaten dezelfde fysieke draden te laten delen. Dat is precies hoe I2C werkt - de twee buslijnen worden omhoog getrokken door weerstanden en de apparaten erop gebruiken open afvoeroutputs om de lijn laag te trekken om hem weer te hoog te zetten om de verschillende logische niveaus te creëren. Als twee apparaten het samen laag trekken, is het net laag. Zonder de open drain-methode als je had dat een apparaat een 1 en een ander uitvoerde met een 0, zou je in principe een kortsluiting krijgen tussen de twee en zou je uiteindelijk chips beschadigen.

En dan heb je natuurlijk het concept van synchrone versus asynchrone communicatie, maar dat is een heel ander soort vis. Simpel gezegd, protocollen met een klok, zoals SPI en I2C, die een master hebben die die klok genereert, zijn synchroon. Protocollen zoals de UART en RS-232, RS-485, enz. Zijn asynchroon - ze vertrouwen op beide uiteinden afspreken hoe snel gegevens worden verzonden (baudrate), zodat ze weten hoe de signalen te interpreteren als ze aankomen.

2
toegevoegd
@ EdgarBonet weet u of de draadbibliotheek multi-master ondersteunt? Is het zo eenvoudig als dit michael.bouvy.net/blog/en/2013/05/25/…
toegevoegd de auteur user7461, de bron
@waspinator: ik kan het niet vertellen, zoals ik nog nooit heb geprobeerd. Het lijkt erop dat de Wire-bibliotheek behandelt busarbitrage , maar ik weet niet of het de problemen aankan die ik eerder heb genoemd.
toegevoegd de auteur Sprogz, de bron
Ik zou er alleen aan willen toevoegen dat I2C in theorie geschikt is voor meerdere moeders en een arbitrageschema heeft dat geen willekeurige getallen nodig heeft. Er lijkt echter een probleem te zijn met de implementatie van Atmel. Vgl bijvoorbeeld TWI-module lijkt buggy in multi-mastercommunicatie en Multiple Master Problem with Atmel AVR Microcontrollers . Single master + interrupts lijkt me de gemakkelijkste oplossing.
toegevoegd de auteur Sprogz, de bron
@ EdgarBonet Vrijwel alle protocollen kunnen multi-master worden gemaakt als een of andere vorm van busarbitratiesysteem wordt geïmplementeerd. Sommigen zijn hier meer geschikt voor dan anderen, en sommigen, zoals I2C, hebben "officiële" manieren om het te doen, waarvan sommige beter werken dan andere. De meest betrouwbare manier is natuurlijk om helemaal geen multi-master systeem te hebben :)
toegevoegd de auteur Majenko, de bron

Bekijk PJON - https://github.com/gioblu/PJON . Het is een one-wire alternatief.

1
toegevoegd
Voeg wat informatie toe over waarom het beter is dan de alternatieven/waardoor het opvalt. Bedankt!
toegevoegd de auteur hintbw, de bron
PJON is beschikbaar gemaakt voor meerdere platforms, niet alleen Arduino. Het is eenvoudiger dan I2C/one-wire. Het behandelt geluid beter. Het heeft multi-master ondersteuning. Lees de wiki van de link.
toegevoegd de auteur Peter Green, de bron
Kunt u alstublieft een antwoord geven op uw antwoord, eventueel een voorbeeld van gebruik of voors en tegens, aangezien dit in werkelijkheid slechts een antwoord is op dit moment.
toegevoegd de auteur RSM, de bron

Naast het uitstekende antwoord van @ Majenko, moet u nadenken over wat u communiceert.

Het klinkt alsof je een enkel beetje wilt communiceren - misschien kun je wegkomen met individuele pinnen? Als je een master hebt en maximaal 13 slaves, kun je een pin op de master hebben aangesloten op een pin op elk van de slaves (via een weerstand). Als slaaf A zijn signaal wilde signaleren, zou het de speld verhogen; de meester zou dan in staat zijn om elke pen om de beurt te peilen, om uit te zoeken wie wat zei.

0
toegevoegd
sorry, dat laatste stukje was slechts een voorbeeld van hoe ik zou willen dat apparaten gegevens naar elkaar zouden kunnen "duwen" in plaats van ernaar te vragen. Ik wil eigenlijk meer gegevens verzenden. ~ 100 bytes 10 keer per seconde.
toegevoegd de auteur user7461, de bron

Een andere optie is SPI.

SPI kan met een willekeurig aantal apparaten in een ring worden gebruikt. Het werd mij als volgt uitgelegd: elke deelnemer kan de kloklijn 8 keer schakelen, om de byte in zijn eigen buffer, in de buffer van het volgende apparaat, in een ring te verplaatsen; het apparaat krijgt een byte van het vorige apparaat. Als iemand anders de klok 8 keer schakelt, krijgt u een melding dat de nieuwe byte gereed is (dit gebeurt in hardware).

Je moet goed nadenken over een protocol hiervoor - een "adres", "lengte", "commando" en dan een willekeurig aantal parameters. De afzender zendt de byte "adres", stelt de "lengte" -byte in de buffer in en houdt de rest gereed. Het volgende apparaat heeft de byte "adres" ontvangen en ziet of het de bedoelde ontvanger is. Als dat niet het geval is, noteert dit dit en duwt de ketting rond, noteert de lengte en wacht totdat iemand anders zoveel bytes heeft rondgeduwd. Als het IS van jou is, lees dan de rest van de gegevens, duw de gegevens rond en onderdruk het terwijl je onderweg bent (om te voorkomen dat het terugkomt). Een leeg adres (bijvoorbeeld 0) zou worden gereserveerd, wat betekent "doe hier niets mee, wacht tot iemand anders het rondduwt, en lees de volgende byte als het volgende adres". Alle apparaten moeten bij het opstarten naar 0 initialiseren.

Als u adressen dynamisch moet instellen, stelt u een speciale byte in voor "alle stations", met een opdracht die betekent "uw apparaatnummer is X, en vertel het volgende apparaat dat het apparaatnummer X + 1 is". Iemand zou dit uiteraard moeten initiëren.

U loopt ook het risico van botsingen, als twee apparaten tegelijk beginnen te verzenden, of als iets niet doorgeeft wat het verondersteld wordt (bijvoorbeeld een chip is uitgeschakeld). U moet nadenken over hoe u dit gaat detecteren/herstellen.

0
toegevoegd