[C] Inviare dati ad Arduino

Ciao a tutti,
pensavo di non aver problemi con la parte relativa alla programmazione di Arduino ma sto avendo qualche difficoltà.

Sto provando un semplice programma che invia un intero ad Arduino ed in base al valore Arduino attiva un pin (relè).

Il codice del programma in C è il seguente:

char serialPort[] = "COM8";

int main(){
    FILE *porta;
    porta = fopen(serialPort,"w");  //Opening device file
	if(porta == NULL){
		printf("\nErrore");
		abort();
	}
     fprintf(porta,"%d",1);
     fclose(porta);
}

E lo sketch in Arduino è il seguente:

#define rele1 1

void setup() { 
    Serial.begin(9600);
    pinMode(rele1,OUTPUT);
    digitalWrite(rele1,HIGH);
 }

int input;

void loop() {
    if(Serial.available()>0){
       input=Serial.read();
       if(input==1){
         digitalWrite(rele1,LOW);
         delay(1500);
         digitalWrite(rele1,HIGH);
       }
     }
}

Non capisco cosa mi sfugge.

Grazie in anticipo a chiunque voglia aiutarmi!

if(Serial.available()>0){ input=Serial.read(); if(input==1){

Devi convertire input in numerico:

input=Serial.read(); input-=48; if(input==1){

Non va nemmeno. Ricevo un valore molto alto, compreso da 200 e 1000: se faccio if(input>200) si attiva il pin.

Cos'altro sbaglio?

Il valore trasmesso in che formato è ?
ASCII, BYTE, WORD, FLOAT ?

ExperimentUno: Il valore trasmesso in che formato è ? ASCII, BYTE, WORD, FLOAT ?

Nel codice che ho postato qui invio un int (%d), ho provato ad inviare anche un char o una stringa. Ma mi da sempre problemi.

Presumo che hai un Arduino UNO.

L'errore é nella prima riga: "#define rele1 1" Il pin 0 e 1 sono usati dalla seriale.

Usa un altro pin per il relá. Ciao Uwe

uwefed: Presumo che hai un Arduino UNO.

L'errore é nella prima riga: "#define rele1 1" Il pin 0 e 1 sono usati dalla seriale.

Usa un altro pin per il relá. Ciao Uwe

Si, ho un Arduino (Genuino) UNO Rev3. Però in realtà utilizzo i ping digitali dal 2 in poi (quel #define rele1 1 l'ho aggiunto dopo aver copiato il codice solo per completezza e non ho fatto caso al fatto che appunto quel pin non lo utilizzavo).

Comunque non funziona proprio. Se dal monitor seriale invio il valore, invece, funziona. Quindi evidentemente è un problema nella parte del codice in C oppure nella "conversione" del valore che riceve Arduino. Qualcuno ha quelle due righe di codice che permettono di far ciò che funzionino? Ho cercato ovunque ma non trovo nulla di diverso da ciò che ho fatto.

Grazie!

Ma siamo sicuri che su Windows la porta seriale si possa usare come se fosse un file? A me pare proprio di no, quella è una prerogativa dei sistemi Unix e Unix-like! Secondo me se cerchi ti trovi da qualche parte un file che si chiama COM8 con dentro dei numeri :).

SukkoPera: Ma siamo sicuri che su Windows la porta seriale si possa usare come se fosse un file? A me pare proprio di no, quella è una prerogativa dei sistemi Unix e Unix-like! Secondo me se cerchi ti trovi da qualche parte un file che si chiama COM8 con dentro dei numeri :).

Si come ho scritto ho anch'io dei dubbi sulla parte in C. Però il collegamento avviene ed invio dei dati: mettendo un else dopo quell'if la scheda risponde. Nessuno ha mai inviato un intero ad una scheda Arduino utilizzando Windows (e magari C, ma va bene anche C++) ?

Che compilatore stai usando?

Se lo invia, dall'altra parte ti arriva una stringa, e devi quindi convertire in intero come dice busco (anche se io userei l'apposita funzione atoi()). In alternativa prova a scrivere con fwrite().

SukkoPera: Che compilatore stai usando?

Se lo invia, dall'altra parte ti arriva una stringa, e devi quindi convertire in intero come dice busco (anche se io userei l'apposita funzione atoi()). In alternativa prova a scrivere con fwrite().

Utilizzo Visual Studio. Comunque ho provato anche con fwrite ma non funziona. Ho provato a convertire il valore ma non funziona nemmeno (anche perché non capisco che valore arrivi, dato che non posso usare contemporaneamente il monitor seriale e la console).

Un pò di debug. 1. per provare solo la parte Arduino basta che i dati li spedisci da Serial Monitor di Arduino IDE Logicamente come già detto quello che spedisci sono numeri ma in testo, quindi non spedisci 1 ma '1' come carattere quindi 48 come valore del byte

  1. poi aggiungi nel codice Arduino un bel Serial.println() del dato che ricevi ovvero input, per capire cosa sta spedendo.

  2. non saprei se da C con quei semplici comandi ha una velocità della porta (baude rate) fissata a 9600. mi pare che quei parametri manchino. a)Questo esempio ma in C.NET usa la classe serialport che ha il settaggio della velocità http://www.instructables.com/id/Serial-Port-Programming-With-NET/ b)Qui un esempio in C puro usando seriale come file, ma sotto windows usa l'API di windows per poter settare i parametri: http://stackoverflow.com/questions/11765349/interacting-with-a-serial-port-using-the-c-functions-fopen-fread-and-fwrite

SukkoPera: Ma siamo sicuri che su Windows la porta seriale si possa usare come se fosse un file?

Assolutamente si, in C la porta seriale è un file indipendentemente dal sistema operativo, poi è il compilatore che gestisce la cosa a livello hardware e software in base al s.o.

astrobeed:
Assolutamente si, in C la porta seriale è un file indipendentemente dal sistema operativo

dipende

In Borland TurboC (ma anche Microsoft C) sotto DOS la gestisce delle seriali l’hanno proposta in almeno due modi.

C’e’ una primitiva nelle librerie a corredo scritte da Borland che richiamano delle subroutine del BIOS, il tutto poi mascherato da stdio (standard I/O, definito da #include <stdio.h>), quindi vedi

       int fgetc(FILE *stream);
       int fputc(int c, FILE *stream);

tipico approccio a stream usato anche a UNIX.

Pero’ spesso de-facto in tanti e tanti listati pubblicati sulle riviste o codice di alcuni progetti (tra cui App.!PC per Gemini-II, guest-x86 card Acorn, lato support DOS) sono state usate librerie che accedono direttamente all’hw, ai tipici indirizzi dove sono mappate le prime quattro porte seriali previste dal progetto Personal Computer IBM

COM1 0x3F8
COM2 0x2F8
COM3 0x3E8
COM4 0x2E8

PORT1 e’ l’address della porta seriale scelta tra le 4 qui sopra

/* Communication Settings */
outportb(PORT1 + 3 , 0x80); /* SET DLAB ON */
outportb(PORT1 + 0 , 0x03); /* Set Baud rate - Divisor Latch Low Byte */

/* 0x01 = 115,200 BPS */
/* 0x02 = 57,600 BPS */
/* 0x03 = 38,400 BPS */
/* 0x06 = 19,200 BPS */
/* 0x0C = 9,600 BPS */
/* 0x18 = 4,800 BPS */
/* 0x30 = 2,400 BPS */

outportb(PORT1 + 1 , 0x00); /* Set Baud rate - Divisor Latch High Byte */
outportb(PORT1 + 3 , 0x03); /* 8 Bits, No Parity, 1 Stop Bit */
outportb(PORT1 + 2 , 0xC7); /* FIFO Control Register */
outportb(PORT1 + 4 , 0x0B); /* Turn on DTR, RTS, and OUT2 */

Segue l’I/O vero e proprio. Periferiche mappate in spazio I/O. Questa cosa (per certi versi una schifezza) e’ tipica dei PC, a sua volta eredita’ degli Z80 ed 8080, quest’ultimo bis bis nonno degli attuali x86. Le macchine RISC non e’ detto che possano farlo, a volte non lo fanno proprio perche’ tutto e’ mappato in spazio ram, lineare e pulito. I PC hanno invece un secondo spazio fisico dedicato alle periferiche, difatti TurboC sotto DOS usa le port.byte functions.

ch = inportb(PORT1); /* Get Char from the Serial Port */
...
outportb(PORT1, ch);} /* Send Char to Serial Port */

Insomma questo codice-approccio-B non e’ portabile, credo funzioni solo sotto DOS e non oltre windows98. E’ un de-facto solo perche’ largamente usato e diffuso in rete negli anni 90 e primi del 2000, ma decisamente non e’ la strada pulita di gestire le seriali.

Conclusione

Fai stra-bene a tagliare corto verso la soluzione piu’ pulita e portabile possibile :smiley:

Infatti "Indipendentemente dal sistema operativo" mi sembra un po' troppo ottimistico, comunque se su Windows/Visual Studio è così, tanto meglio.

SukkoPera: Infatti "Indipendentemente dal sistema operativo" mi sembra un po' troppo ottimistico

Non è troppo ottimistico, se parliamo di Ansi C vero è così, poi se qualche compilatore si prende delle libertà, diverge dallo standard, questo è un altro paio di maniche. Per quanto ne so io in ambiente Windows, tutte le versioni a partire da Win 98, i compilatori di Microsoft e Borland (ora Embarcadero) hanno sempre permesso di usare la seriale come file, poi ci sono anche delle librerie ottimizzate, legate ad uno specifico sistema operativo e hardware, che mettono a disposizione funzioni dedicate per l'uso della seriale. Del TurboC per DOS ricordo poco, è una vita che non lo uso più, il modo di accesso indicato da Dally è l'equivalente di quello che si fa adesso con le MCU quando si passa direttamente per i registri delle periferiche hardware, cosa più che logica anche con i processori X86 visto che anche se sono dei crisp le varie periferiche, esterne al micro vero e proprio, sono comunque mappate sotto forma di registri.

SukkoPera: Infatti "Indipendentemente dal sistema operativo" mi sembra un po' troppo ottimistico

In teoria e' fattibile a patto che chi fornisce la toolchain si sia preso la briga di scrivere bene le varie primitive base dello standard I/O senza partire per la tangente. Costa un layer, e costa un po' piu' di fatica (per loro), pero' poi rende il codice un zichinello piu' portabile.

Notare che sistemi operativi come OS/2 Warp3 si inRazzano come dei ricci se una applicazione DOS prova a toccare le port-IO senza chiederne il permesso, ed arrivano subito le bastonate: applicazione DOS abortita.

La stessa cosa vale per WindowsNT e suoi derivati: l'OS vuole che la App faccia esplicito uso di servizi kernel appositi. Guai ad accedere direttamente all'hw, per cui questo e' un valido incentivo per scrivere bene lo standard I/O con le brave primitive di cui sopra.

Al loro interno ci sono chiamate a servizi kernel. E l'OS e' felice.

Anche linux e tutti gli UNIX hanno la stessa motivazione. Difatti se proprio uno volesse accedere direttamente all'hw (come si faceva col DOS, esempio qui sopra) dovrebbe scrivere un kernel module (un driver) apposito.

(tralascio alcuni trucchi porcherie per aggirare la cosa. Pericolossissimi)

Con Windows pero' c'e' proprio la cattiva abitudine di fare oggetti di sistema, fisicamente delle DLL caricate e linkage dinamicamente con il kernel (tipicamente nelle cartelle system e system32), E questa cosa, tipicamente usata e motivata da Borland Delphi, un dialetto del Pascal, ha causato altri fork dello standard I/O.

Nel caso specifico fino al 2004 andava di moda usare un oggetto chiamato COM, con dei metodi, get, put, setbaud, etc, il quale era facilissimo da usare: ciccavi sul bottone, il tool te lo appoggiava sul form, ci ciccavi sopra una seconda volta per definirne le proprietà, Et voilà le jeux, anzi, una volta invocato nel resto del codice si preoccupava lui di richiamare il corretto servizio kernel lato Windows.

Fantastico? Beh, faceva da passacarte, da wrapper. Bello (forse), pero' se lo usi nel tuo codice applicativo poi non e' portabile!

Difatti oggi Delphi e' RIP, se si vuole riutilizzare il vecchio codice, magari migrarlo su FreePascal + Lazarus, tocca rivederlo tutto da capo mandando in pensione COM, da rimpiazzare altro codice.

La cosa ironica e' che alcuni utenti Visual C hanno iniziato a copiare l'oggetto COM del Delphi, scrivendone un clone per il loro ambiente C. Altra cattiva abitudine che e' andata avanti per un po'.

Mi chiedo, ma ha senso riscrivere le cose n-mila volte? :D

astrobeed: cosa più che logica anche con i processori X86 visto che anche se sono dei crisp le varie periferiche, esterne al micro vero e proprio, sono comunque mappate sotto forma di registri.

Dal punto di vista architetturale pero' e' una pura questione legacy. C'era su z80, c'era su 8080, l'ha ereditato x86, e ce lo portiamo dietro come una palla al piede. Ti faccio notare che avere istruzioni I/O diverse dalla load/store nella ISA rompe non poco le scatole ai progetti superscalari, sopratutto quando tocca andare fuori ordine. Tocca aggiungere un secondo stadio con un sacco di roba lungo il datapath per l'enforce in-order execution of IO, e questa cosa aggiunge altra complexity al progetto della CPU.

Tanto di cappello agli ingegneri Intel che riescono a non farsi venire il mal di testa, o a buttarsi dalla finestra per la disperazione :D

Grazie intanto per tutte le risposte.
Rispondo:
1- ho fatto i debug con il monitor seriale per verificare la parte Arduino: inviando un valore, Arduino risponde correttamente ed inoltre con Serial.println() verifico che il valore viene letto correttamente.
2- il modo per collegarmi alla seriale sembra corretto: di fatto quando invio un intero/char, Arduino riceve qualcosa e risponde. Il problema è capire cosa arriva anche perché a volte funziona ed altre no: ho provato ad inviare un char ‘a’ e le prime due volte funzionava (entrando nel ciclo if(input=‘a’) e poi è entrato nell’ else; sembra tutto regnato dal caso, non ha senso.

E’ quindi possibile inviare dati ad Arduino tramite C?
Possibile che nessuno abbia mai utilizzato il C per comunicare con Arduino?? (Su internet di fatto non ho trovato assolutamente nulla!)

Grazie!

Nel tuo caso dipende cosa hai cercato. In rete e’ pieno di esempi. Basta cercare bene con la giusta parola di ricerca. Di Windows moderno pero’ ne so meno di zero.

Quindi ti propongo il piano B: installati una macchina virtuale sotto VirtualBox, installati linux nella macchina virtuale, collega una periferica usb-serial, mappala sulla VM; a quel punto puoi rifarti agli esempi linux in C.