PJON - Multi-master, multi-media network protocol stack

Ciao a tutti, ciao Paolo! :slight_smile:
Come puoi vedere ho accettato la tua pull request e modificato estensivamente la libreria. Attualmente in master sono finalmente presenti le modifiche a cui stavo lavorando in locale da tempo. Finalmente esiste un readme decente e degli esempi funzionanti. Grazie per il tuo supporto.

Posso dire di essere vicino alla release della 1.0 :o :o

Cosa ne pensate???

Ho fatto la richiesta (spero non ti dispiaccia) per l'inserimento nel library manager.
Però devi creare una release.
--> Release Your Software | The GitHub Blog

Mi serve un po' di tempo per testarla e comunque ho un probabile progetto dove utilizzarla.

La libreria è stata aggiunta.
--> [library] Please add PJON to manager · Issue #3837 · arduino/Arduino · GitHub

Come già detto manca la release. :slight_smile:

Ciao Paolo, grazie del tuo supporto!! :slight_smile: :slight_smile:
Sono stato molto preso dal lavoro e nel gestire tutte le richieste, commenti e proposte relative a PJON grazie a un post su hackernews rimasto in prima pagina per piu' di un giorno :slight_smile: . Mi hanno chiesto molto insistentemente di documentare lo "Standard" PJON per permettere una facile implementazione o porting su altri dispositivi. Ci sono gia 3 issues per compatibilita' ARM, raspberry e attiny.

Qui trovate la wiki:

Cosa ne pensate?

Mi pare di capire che, per distinguere i singoli bit in arrivo, non usi il classico oversalpling (quello che si usa nella RS232 per intenderci) ma ti affidi "solo" a dei loop di attesa.

Dovresti provare a mettere nel bus una scheda con il clock volutamente un po' fuori tolleranza (a parte cambiare quarzo, ci in genere ci sono modi per variare il clock tramite i registri interni dei microcontrollori, credo che questo valga anche per Arduino) e vedere cosa capita.

Specie se intendi fare il porting (o approvare ufficialmente porting fatti da terzi) su altri microcontrollori, ti accorgerai che il clock nel mondo reale può avere forti tolleranze dovute a fattori su cui non puoi avere controllo. E' necessario che la libreria sappia deformare (entro certi limiti) ed in tempo reale i tempi di attesa per riconoscere gli "1" dagli "0".

Nel caso di errori dovresti implementare quello che per la RS232 è il frame synchronization.

Ti suggerirei (ma questo non è un bug ma una proposta per migliorare la libreria) di prevedere qualche forma di auto-indirizzamento. Almeno prevederla a livello di protocollo. Poi se e come usarla puoi farlo in un secondo tempo.

Grazie degli utili consigli ed e' un piacere vedere che tu ti sei interessato e abbia letto lo standard e la documentazione. Allora, quello di cui sono abbastanza convinto e' che per esempio, la raspberry avra' una "percezione del tempo" diversa da un arduino e quelli che sono 10 microsecondi per arduino potrebbero essere 8 o 12 per la rasp (dico numeri a caso). Per ovviare al problema il primo approccio a cui ho pensato e' variare la definizione della durata di un bit nella raspberry fino ad ottenere una durata identica a quella generata dall'arduino. A quel punto nel porting avrai una definizione di lunghezza del bit diversa da quella presente nell implementazione dell arduino che pero' avra' effettivamente la stessa durata. Un'altra soluzione e' di certo quella proposta da te. Cosa ne pensi?

In teoria non fa una piega, in pratica però c'è da testare un po' di cose.

Raspberry non è un microcontrollore semplice ma ha un layer software molto complesso (ci gira linux, non quattro routine C in croce come siamo abituati con i micro a 8 bit...)
Ne consegue che una implementazione software delle temporizzazioni potrebbe essere o estremamente precisa (ci sono risorse in abbondanza per gestirla) o estremamente scostante (perché qui hai un vero multitasking e quindi le temporizzazioni seguono concetti base molto differenti), dipende da come è scritta la routine di delay e da come la distribuzione linux gestisce il multitasking.

Hai buone probabilità che la tua soluzione funzioni, ma non potrei scommetterci su. Devi provare.

Tutte le problematiche in relazione al time-shift vengono risolte con un hardware appropriato che con contatori multipli al data-rate compensano differenze dovuti a quarzi da 50-200ppm e impedenze di linea dati, quì invece siamo di fronte a un protocollo totalmente software basato su un hardware standard minimale, per tenere leggero il carico software del protocollo una certa rigidezza sul timing penso sia più che tollerabile

Mah, l'versapling sulla rs232 veniva fatto via software sugli ST6 a 8 bit... non è niente di complicato. Si legge lo stato di una porta più volte al secondo di quanto servirebbe in teoria, e poi si conta se il passaggio da zero a uno e viceversa è capitato prima o dopo una certa soglia.
Alla fine è una manciata di righe di codice.
Certo bisogna vedere se davvero serva o no (per questo chiedevo un test esasperando un po' l'errore del quarzo), ma quanto a "carico software" si tratta di un impatto minimo.

Ciao! Sto ragionando a una nuova parte di questo standard che permetta la creazione di un unica grandissima rete, fatta di n bus con max 255 nodi, tutti connessi assieme tramite routers (un po' come internet).

Per supportare la possibilita' di comunicare da qualsiasi nodo di un bus a un altro nodo di un altro bus e' necessario:

-sapere identificativo univoco bus (BID)
-sapere identificativo univoco pacchetto (PID)
-sapere identificativo univoco device destinatario (DID)
-Il router deve conoscere gli identificativi dei bus ai quali e' connesso

quindi un pacchetto potrebbe essere fatto cosi:

simbolo pacchetto outbound (non per un device all'interno del bus locale) 1 byte
BID (4 byte)
DID (1 byte)
PID (2 byte)
contenuto
CRC

Il pacchetto verra' inviato al router presente nel bus del device trasmittente. Questo lo inviera' ai router presenti nei bus ai quali e' connesso e cosi' via, finche' uno dei router che ricevera' il messaggio all'interno della rete avra' nella lista degli identificativi bus conosciuti quello richiesto. A questo punto dovra' solo inviare un pacchetto locale (quello con cui ora funziona PJON) e comunicare all'identificativo del device all'interno del bus. Un pacchetto ACK partira' dal destinatario verso il mittente, e fara' la strada inversa all'interno della rete contenendo ACK e l'id del pacchetto cosi' da permettere il mittente di avere la sicurezza di aver ricevuto il messaggio.

Cosa ne pensate?? Offrendo una struttura del genere se un sacco di gente lo usasse, sarebbe possibile creare un sistema come APRS per comunicare pacchetti dovunque sia presente la rete.

Ohhh finalmente adesso PJON ha un porting stabile funzionante anche con i moduli radio ASK 433Mhz!!

Fatemi sapere cosa ne pensate!! Con queste due librerie e' possibile realizzare un router che puo' spostare pacchetti in arrivo da radio a filo o ancora peggio su internet (includendo una libreria per ethernet shield), un po' come funziona APRS!

Pero' devo decidere come definire i pacchetti outbound come dicevo nella risposta precedente per poter definire questa parte.

Praticamente stai reinventando Ethernet, TCP e IP :smiley:

Scherzi a parte, quel che voglio dire è che stai facendo un lavoro molto promettente e sicuramente arricchirà molto il tuo bagaglio culturale.
Quindi, dal punto di vista "teorico" meriti sicuramente un encomio.

Dal lato strettamente pratico però devi tenere presente che sarà ben raro che una rete "casalinga" necessiti di centinaia di nodi.
Se proprio ad un progettista toccasse un simile problema, con ogni probabilità si affiderebbe a protocolli e bus fisici stra-noti e affidabili come quelli che ho citato, anche perché è un investimento lato hardware non indifferente e poter contare su parti "di serie" convincerebbe meglio il committente.

Stai rischiando di scoprire l'acqua calda o peggio di perdere tantissime ore a reinventare la ruota dandogli solo un altro nome.
Finché lo fai per cultura personale non ho niente da dire, ma non ti aspettare un grande successo del tuo protocollo quando la rete è così estesa. Successo che ovviamente ti auguro, sia chiaro, ma non ti nascondo il mio scetticismo riguardo il numero di possibili progetti che lo coinvolgeranno.

Anche se facessi tutto alla perfezione, il bus è relativamente lento: ottimo perché più è lento, meno errori di comunicazione hai. Ma quando pensi a centinaia di oggetti interconnessi, i tempi per inviare il messaggio si allungano (perché si presume che più oggetti interconnessi, più sia alta l'occupazione media del bus, mica saranno tutti moduli in attesa di istruzioni quelli che aggiungi). Non puoi premere un pulsante e accendere la lampadina 10 secondi dopo...

Oggigiorno tra reti cablate o radio già molto ben strutturate, in cui si sono già ben affrontati tutti i problemi di affidabilità della comunicazione, criptazione, auto routing in caso di nodi spenti, resilienza ai disturbi ecc ecc c'è più di una scelta "professionale" a costi relativamente contenuti.

Quel che manca è un modo di fare in modo economico, affidabile e "moderno" piccole reti di microcontrollori.

Ti suggerirei quindi di approfondire il mondo delle piccole reti. A PJON serve un layer software per l'auto indirizzamento delle periferiche, servono numeri reali (indicando esattametne le condizioni di test) sul reale transfer rate (hai indicato quasi tutti numeri teorici e hai un solo impianto di test a quanto pare), forse serve un sistema di correzione delle derive del clock, puoi potenziare la parte di checksum e auto retrasmit dei dati corrotti... Insomma, il prototipo è perfetto ma di lavoro ce n'è ancora tanto.
Va benissimo riservarsi qualche byte nel protocollo per future espansioni come router ecc, ma lascia il tutto proprio solo a livello di spazio per future espansioni.

Per farla breve, date a Ethernet quel che è di Ethernet e date a PJON quei che è di MODBUS :smiley:

Ok, ho capito. Non sei il primo che me lo dice.
In ogni caso fare in modo che supporti una rete di reti non e' cosi' complicato al massimo scrivero' un layer a parte dello stack in futuro :). In ogni caso, cosa ne pensi dell'implementazione radio che ti ho linkato? Credo sia una discreta alternativa a VirtualWire! :slight_smile:

Ho un paio di quei modulini in un cassetto, nel prossimo weekend farò un test.

Mi pare molto promettente, ed è appunto quel che intendevo quando ti suggerivo di focalizzarti sulle piccole reti a basso costo.

Con una libreria del genere si riesce a far fare cose a quei modulini che fanno quasi solo gli xbee, ma ad una frazione del prezzo totale

Alcuni utenti mi richiedono l'auto addressing dei nodi, tu cosa ne pensi? soprattutto per la versione radio sarebbe molto utile.

C'è più di un modo di farlo e devi valutare bene quale strada sia migliore.

Indirizzamento automatico significa sapere gestire i conflitti sugli indirizzi, specie se non dedichi un nodo ad essere un unico coordinatore degli indirizzi assegnati.

Secondo me potresti pensare a una qualche forma di auto indirizzamento minimale, con minimi impatti sulla libreria e lasciare il grosso del codice ad una applicazione di esempio. Così solo chi vuole la implementa nel suo codice.

Ad esempio attivare un comando "ping" che consumi pochi byte sul bus e a cui la libreria risponda sempre in automatico (senza che l'utilizzatore della libreria debba scrivere la parte software per la risposta).

Poi nella libreria fornisci una implementazione di esempio:

Al setup verifichi l'indirizzo presente in eeprom

  • se è 0x00 o 0xFF allora vuole dire che la eeprom è intonsa (mai attribuìto un indirizzo). Parte allora un ciclo in cui si "pinga" il bus da 0x01 a 0xFE. Al primo ping che va in timeout, allora significa che quell'indirizzo è libero. Lo inserisce in eeprom.

  • se non lo è, recupera l'indirizzo. Poi prova a pingarlo (la libreria deve evitare di auto-rispondersi, ma deve poter trasmettere fuori il proprio stesso indirizzo). Se nessuno risponde allora vuole dire che quell'indirizzo è ancora libero e continuerà ad usarlo. Se qualcuno risponde, allora significa che c'è un conflitto degli indirizzi (ad esempio un modulo è stato acceso mentre questo era spento). Con il ciclo di prima recuperi un nuovo indirizzo libero. Preferibilmente informi il bus (con un messaggio broadcast) che c'è stato un conflitto (indicando vecchio indirizzo e nuovo indirizzo), così i moduli che ad esempio si aspettavano un sensore di temperatura all'indirizzo 0x03 ora sanno che se lo troveranno al 0x05)

In realtà ci sono metodi anche molto più evoluti per gestire indirizzi e conflitti, ma questo mi pare abbastanza minimale da essere adatto al tipo di rete.

Una alternativa potrebbe essere che ogni modulo conserva una copia della lista degli indirizzi assegnati, quindi ciascuno sa sempre qual è il primo indirizzo libero: all'accensione fai il ping in loop, ammettiamo che il 0x01 non risponda, tu continui con il 0x02. Questo risponde al ping e ti dice che il primo libero è 0x05. Tu ti prendi 0x05 e con una chiamata broadcast dici che 0x05 è assegnato ( e tutti i moduli si segnano che non è più libero). Così hai evitato un conflitto su 0x01, 0x03, 0x04: moduli esistenti ma spenti o non raggiungibili in quel momento. All'accensione un modulo deve scaricarsi la lista aggiornata dal primo modulo che trova, in modo da avere una lista "fresca".
Questo metodo ha il vantaggio di essere molto efficace nell'evitare conflitti, ma consuma un po' di ram (almeno 32 byte solo per la lista).
Ovviamente bisogna anche prevedere il comando "disconnetti dalla rete" per informare il bus che l'indirizzo XYZ è nuovamente libero.

L'opzione di avere un coordinatore unico per tutto il bus è simile alla precedente ma c'è un solo microcontrollore che ha la copia della lista. Questo modulo risponderà sempre ad un indirizzo predefinito (es 0x01). In questo modo gli altri nodi sono più semplici, ma se il primo nodo non è raggiungibile, allora tutta la rete non accetta più aggiunte o rimozioni di moduli dalla rete.

Ciao Nabia. Grazie dei consigli.

Io rimarrei su modalita' multimaster, (quindi senza un unico device in grado di fare l'addressing).
Quello a cui pensavo e' che quando un device si connette a una rete, estrae un numero casuale e pinga a quell'ID, finche' non ne trova uno libero. Se riceve un messaggio spedito con come mittente il proprio ID (anche dopo ore che si e' trovato il suo ID), dovrebbe abbandonare l'ID attuale e ricercarne uno nuovo.

Cosi' alla connessione di un device potresti avere una leggera perdita di banda, ma avresti dalla tua la cosa positiva di non doverti ricordare tutti gli id e non dover avvisare di esserti disconnesso.

Eh sì ma il termometro che è "abituato" a sevegliarsi ogni 10 minuti e mandare la lettura della temperatura all'id 0x45 come fa a sapere che mentre dormiva è cambiato id ed ora deve mandare a 0x98?

Sopratutto se entri nel mondo della comunicazione radio è la norma (e non l'eccezione) che un dispositivo vada in sleep per contenere l'uso delle batterie quando non è strettamente necessario che sia acceso.

Guarda che la gestione dinamica dell'indirizzamento non è così semplice quanto sembri. Proprio per questo ti suggerivo di non mettere troppa roba nella libreria e affidarti ad un esempio, così si può personalizzare in base alle peculiarità della propria rete.

Ciao a tutti,

Questa libreria è ottima ma secondo me ha un problema di fondo:

void loop() {
network.receive(1000);
}

non si ha mai la certezza che il dato sia ricevuto nel reciver.

Non si potrebbe usare un interrupt e mettere un su buffer circolare i pacchetti ricevuti da leggere con comodo quando si ha tempo per farlo?

La certezza ce l'hai. Come spiegato nel readme, devi usare il setter "set_receiver" passando come parametro la funzione che verra' chiamata ogni volta che un messaggio sara' correttamente ricevuto.
Il bello e il cuore di questa libreria e' proprio che non usa interrupt o timer hardware :slight_smile: