Programmazione orientata agli oggetti con Arduino

Salve ragazzi oggi vi chiedo se gentilmete potreste spiegarmi la programmazione orientata agli oggetti in Arduino poichè sono uno studente di informatica ed abituato a programmare in java vorrei iniziare a programmare similmente anche in arduino! Più precisamente anche come funziona ad esempio la creazione di nuove classi come ad esempio ritroviamo in java

Studiati il C++ visto che il linguaggio di programmazione di arduino è molto simile. Il passo java->C++ dovrebbe risultarti abbastanza semplice (a parte per il discorso sui puntatori) dato che il primo deriva dal secondo.

Nell'ambiente Arduino programmi in C/C++. Java è simile al C++, quindi con un libro per questo linguaggio dovresti trovarti velocemente a tuo agio.
Per la creazione di librerie per l'Arduino ti rimando a questa pagina:
http://arduino.cc/en/Hacking/LibraryTutorial

Altro buon modo per imparare è quello di aprirne una e di vederne il codice.

Attenzione però a pensare di programmare su Arduino così come fai ora in Java, presumo su un PC (con windows o linux o mac).
Lì hai una macchina con minimo 2Gb Ram.
Il motore Java o C# su PC ha la "Garbage collection" ovvero viene fatta pulizia in memoria in automatico dei vari oggetti che vengono creati e poi non più in uso. Difficilmente saturerai la ram, anche perchè se il tuo programma necessita di memoria, il S.O. sfrutta l'HD.
In Arduino non c'e' e lavori su un microcontrollore con 2K ram sulla uno e 8K ram sulla mega (la due non ricordo).

EDIT: e vabbè ho scritto una cavolata, intendevo C# e non C++, ho corretto. :grin:

Il C++ ha il garbage collector? Dove ,che lo voglio pure io!

Non mi pare neanche a me che il C/C++ abbia il "cestino del sudicio" :wink:

leo72:
Non mi pare neanche a me che il C/C++ abbia il "cestino del sudicio" :wink:

Ribadisco il concetto che usare il C++ su una piccola mcu da 8 bit è volersi fare male da soli, ANSI C forever :grin:

astrobeed:
Ribadisco il concetto che usare il C++ su una piccola mcu da 8 bit è volersi fare male da soli, ANSI C forever :grin:

+1 ...

... anche se, PURTROPPO, molte librerie che si trovano in giro sono in C++ e ... l'occupazione di memoria e l'inefficienza si vede XD :grin: :smiley: :grin:

Guglielmo

... anche se, PURTROPPO, molte librerie che si trovano in giro sono in C++ e ... l'occupazione di memoria e l'inefficienza si vede smiley-lol smiley-mr-green smiley-grin smiley-mr-green

È vero, però in certi casi è veramente comodo :slight_smile:

astrobeed:
Ribadisco il concetto che usare il C++ su una piccola mcu da 8 bit è volersi fare male da soli, ANSI C forever :grin:

Vero. Difatti mi sono comprato il K&R anche io :sweat_smile:
Lo leggo la sera prima di dormire :wink:

leo72:
Lo leggo la sera prima di dormire :wink:

Se è per prendere sonno ti consiglio"I fratelli Karamazov di Dostoevskj", dopo due pagine sei già nella braccia di Morfeo :grin:

No, lo leggo la sera perché è l'unico momento della giornata in cui tutti mi lasciano in pace :wink:

Sono sempre più convinto della necessità di un testo come ad esempio un ipotetico "Impariamo a programmare con Arduino" dove invece della parte Hardware si svisceri la parte Software partendo dalle basi del C fino all'uso dei puntatori e delle classi del C++.

PaoloP:
e delle classi del C++.

Tutto bene fino a questo punto che è da eliminare :slight_smile:

astrobeed:

PaoloP:
e delle classi del C++.

Tutto bene fino a questo punto che è da eliminare :slight_smile:

Suvvia non essere così negativo. Se è la DUE qualche classe ci può stare.

Volenti o nolenti, l'Arduino ci va giù di C++ e di conseguenza tutte le lib a seguire.
Si dovrebbe riscrivere il core in C puro ma questo non credo sia possibile :wink:

leo72:
Volenti o nolenti, l'Arduino ci va giù di C++ e di conseguenza tutte le lib a seguire.
Si dovrebbe riscrivere il core in C puro ma questo non credo sia possibile :wink:

Per fare si può, non vedo perchè non si possa scrivere una libreria simil core, il problema non è tanto il linguaggio,
che può fare la differenza in modo marginale o influire marcatamente, tutto dipende da come si usano i linguaggi C/C++.

Anche il C permette la programmazione orientata agli ogetti e ti posso assicurare che se vuoi rispettare tutti comandamenti della OOP, il tutto finisce per
essere veloce quanto il C++. Il C++ non è lento per natura, ma è quello che gli facciamo fare che lo fa diventare lento.

Spesso tutto ciò che è buona tecnica di programmazione OOP peggiora le prestazioni. Se te ne freghi di OOP allora forse il C è la migliore soluzione.

Dai commenti sembra che il C++ sia il male assoluto, e quindi anche arduino visto che buona parte è composta da classi, invece ci sono cose interessanti al fine di riusare il codice, quindi minore spazio del compilato e minore impronta in RAM. C'è anche da considerare che scrivere programmi decenti è più complesso se usi il C++, specie quando si hanno limitate risorse.

In ogni caso penso proprio che buona parte del core contiene ottimo codice finalizzato a favorire la semplicita di utilizzo più che l'efficienza, nota che sono parti della stessa coperta, se tiri verso la semplicità perdi in efficienza e in flessibilità.

Tra le altre cose digitalWrite() è lenta non perchè è scritta in C++, anzi è proprio una comune funzione C. Dall'altro lato usando il C ti puoi scordare la print che stampa più di un tipo, in C dovresti usare una funzione per tipo oppure la printf e le format string che come sapete non sono proprio amichevoli.

Se volete provare il lavoro non è molto, ma ci si deve dividere il compito e sopratutto avere tutti lo stesso orientamento, e per questo la vedo molto dura, ma forse perchè sono influenzato negativamente dagli accadimenti degli ultimi 30 anni. :grin:

Ciao.

MauroTec:
Dai commenti sembra che il C++ sia il male assoluto,

Sulle piccole mcu lo è, ti faccio un banale esempio che dovrebbe chiarire il concetto, se realizzo un programma in ANSI C quando lo compilo so sempre esattamente quanta memoria ram utilizza, se lo fai in C++ non puoi mai saperlo con precisione, sai solo l'impegno "statico", quello dinamico potrebbe facilmente andare oltre la ram disponibile con tutte le conseguenze del caso.
E' vero che la digitalwrite() è una funzione e non una classe, però è scritta male ed è per questo che è molto lenta, invece di un singolo ciclo macchina richiede come minimo oltre 50 cicli macchina.

Il computo della quantità di memoria occupata dal programma in corsa, può essere una incognita difficile da determinare anche in C, tutto dipende dal tipo di programmazione che stai mettendo in essere. Su dispositivi dalle risorse limitate il miglior risultato lo ottieni scrivendo codice specializzato, e quindi non generico, e il riuso del codice tramite funzioni o OOP cessa di esistere.

La digitalWrite() fa delle cose che se vuoi fargliele fare per forza ciò che puoi guadagnare riscrivendola lo perdi ad esempio in semplicità, e comunque sarà sempre un compromesso che se accettato viene considerato il prezzo da pagare. Se la digitalWrite() fosse una macro o funzione inline e se operasse con gli indirizzi delle porte sarebbe molto più veloce e questa avrebbe un valore statico conosciuto, diversamente se devi cercare nella flash una corrispondenza tra il digital 3 e il reale registro si finisce per forza per avere una velocità di esecuzione che dipende da quanto tempo impiega la ricerca.

Segue del codice per discuterci sopra:

inline void gpio_on(volatile uint8_t *dir, volatile uint8_t *out, volatile uint8_t *in,
                    uint8_t bit, int8_t pcInt)
{
    *out |= _BV(bit);       // imposta il bit on
}

Ogni volta devo passare 5 argomenti alla funzione, e ne sarei obbligato se volessi anche attivare la direzione. Il costo in tempo per passare 3 variabili puntatore e 2 per valore è conosciuto, ma il calcolo ora io non lo so fare, ma ammettiamo fosse un tempo accettabile, non è accettabile il tempo che impiego a scrivere la chiamata alla funzione. Per ridurre il tempo che impiego a scrivere la chiamata a funzione si possono accorpare gli argomenti in una define così:

//      Name               DDRx,       PORTx,         PINx,   Bit,   PCINT
#define GPIO_B0     &DDRB,      &PORTB,     &PINB,	0,	0
#define GPIO_B1     &DDRB,      &PORTB,     &PINB,	1,	1

quindi posso chiamare la funzione così:

gpio_on(GPIO_B0);

Oppure potremmo pensare che passare un solo puntatore a struttura costi meno di quanto costa la attuale chiamata a gpio_on(), si in effetti
un solo puntatore costerebbe meno, ma mi comporta un costo di RAM, allora posso pensare di salvare la struttura in FLASH ed avere in ram il solo
puntatore, ma perderei in velocità.

Dal momento che gpio_on() non usa gli argomenti 'dir, in, pcint' potrei eliminarli ma perdo in omogeneità. Il programmatore si aspetta di poter usare
ovunque le macro GPIO_xx es:

inline void gpio_mode(volatile uint8_t *dir, volatile uint8_t *out, volatile uint8_t *in,
                      uint8_t bit, int8_t pcInt, enum pin_mode mode)
{
    // se mode==mode_output imposta il pin come output
    // altrimenti se mode==inp_mode imposta il pin come input
    // altrimenti se mode==inp_pullup_mode imposta il pin come input con pullup

    (mode==out_mode) ? *dir |= _BV(bit)
        : (mode==inp_mode) ? *dir &= ~_BV(bit), *out &= ~_BV(bit)
        : (mode==inp_pullup_mode) ? *dir &= ~_BV(bit), *out |= _BV(bit) : 1;
}

Qui si vede come soltanto 'in, pcint' sono superflui, ma se devo leggere da un pin attraverso una funzione inline mi serve 'in".
Allora con le macro GPIO_xx si guadagna in chiarezza e omogeneità, ma alle volte a seconda delle funzioni chiamate si passano degli argomenti
superflui.

Quindi l'efficienza maggiore la otteniamo scrivendo codice specializzato, evitanto le indirezioni e le facility, ma se nel computo della efficienza ci mettiamo anche il tempo per scrivere il codice e il tempo per mantenerlo la valutazione si complica e in questo contesto sarebbe un errore di valutazione, noi vogliamo ottenere la velocità massima o una molto vicina al massimo.

Quindi, si passano solo puntatori a funzioni, anche per un intero, e se possibile usiamo le variabili globali con tutti i rischi che ne conseguono, si perchè le variabili globali sono considerate il male, causa di bug difficili da scovare, se non impossibili. Ma quando vogliamo la velocità le variabili globali sono l'unica soluzione per manipolare i dati da dentro ambiti con scope interno a quello globale, cioè nel caso di funzioni o in genere in blocchi {} interni.

Tutte queste precauzioni ha senso usarle sempre e comunque, a prescindere dalla necessità di ottimizzare?

Ciao.

MauroTec:
Il computo della quantità di memoria occupata dal programma in corsa, può essere una incognita difficile da determinare anche in C

Assolutamente no, in C ANSI sai esattamente quanta ram serve, o viene impegnata, al termine della compilazione, unica eccezione è se viene usata la malloc() con valori determinati da eventi run time, però è sempre possibile prevedere, e limitare, il range per non sforare la ram disponibile.