Quando Arduino deve comunicare via rete, spesso si sceglie il protocollo HTTP perché consente di utilizzare come client un semplice browser web (IE, Firefox…). A volte però è necessario che Arduino invii dei dati ad altre applicazioni: in questo caso possiamo sfruttare una comunicazione socket.
Questo tutorial è suddiviso in due parti:
- nella prima, vedremo come sviluppare una applicazione socket in C#
- nella seconda, vedremo come scrivere uno sketch per Arduino
Socket server in C#
Il framework .Net mette consente di operare sui socket in due modalità: sincrona e asincrona.
I metodi sincroni sono bloccanti: il thread nel quale sono eseguiti si ferma fino al termine della chiamata. Se ad esempio chiamiamo il metodo Receive() per leggere dati dal socket, tale metodo bloccherà l’esecuzione finché i dati saranno disponibili o finché scatterà un timeout.
I metodi asincroni invece chiedono al framework di gestire l’operazione, indicandogli quale oggetto callback chiamare quanto tale operazione è terminata. Normalmente tali metodi iniziano con Begin (ad esempio BeginReceive()). Programmare utilizzando metodi asicroni è però più complesso: per questo tutorial utilizzerò quindi i metodi sincroni, creando però un diverso thread per non bloccare la GUI del mio applicativo.
Il nostro SocketServer sarà quindi formato da due elementi:
- la GUI (Form1.cs) che consentirà all’utente di interagire con il programma

- la classe SocketHandler (Program.cs) che si occuperà di dialogare via socket con Arduino

Il programma riceverà da Arduino il valore della temperatura rilevata da una sonda e lo visualizzerà sia in forma testuale che in forma grafica (usando una progress bar); è inoltre presente una text area per il log di debug.
La classe Socket
Il framework .Net mette a disposizione l’oggetto Socket per gestire le connessioni in ingresso.
Per prima cosa, includiamo il package System.Net.Sockets, quindi – se l’utente preme il pulsante Start listening – configuriamo l’oggetto Socket perché sia in ascolto sulla porta TCP specificata:
IPAddress listeningIp = IPAddress.Any; IPEndPoint listeningEndPoint = new IPEndPoint(listeningIp, listeningPort); listeningSocket = new Socket(listeningIp.AddressFamily, SocketType.Stream, ProtocolType.Tcp); listeningSocket.Bind(listeningEndPoint); listeningSocket.Listen(10); |
Creiamo una nuova istanza dell’oggetto IPEndPoint specificando l’indirizzo IP su cui essere in ascolto (nel nostro esempio Any, ovvero ogni interfaccia di rete presente) e la porta. Quindi creiamo una istanza dell’oggetto Socket con protocollo TCP. Infine leghiamo (= bind) tale istanza all’endpoint e mettiamola in ascolto, in attesa di nuove connessioni (10 indica il numero massimo di connessioni in coda).
SocketHandler socketHandler = new SocketHandler(listeningSocket, this); serverThread = new Thread(new ThreadStart(socketHandler.startListening)); serverThread.Start(); |
Per evitare di bloccare il resto del programma, creiamo un nuovo Thread in cui eseguiamo la classe SocketHandler che gestirà la connessione in ingresso:
try { // Wait for incoming connection handlerSocket = listeningSocket.Accept(); handlerSocket.ReceiveTimeout = 5000; IPAddress clientAddress = ((IPEndPoint)handlerSocket.RemoteEndPoint).Address; |
Il metodo Accept() è bloccante: il thread resterà in attesa di una nuova connessione… all’arrivo di questa viene impostato un timeout in lettura di 5 secondi e viene letto l’indirizzo IP del client che si è appena connesso.
while (true) { // Save received bytes byte[] buffer = new byte[1024]; int bytesRec = handlerSocket.Receive(buffer); message += Encoding.ASCII.GetString(buffer, 0, bytesRec); // Message completed? Parse it... if (message.Contains("\r")) { myForm.setTemp(message.Replace('.', ',')); myForm.log(" message received: " + message); break; } } |
Leggiamo quindi i dati in ingresso con il metodo Receive (anche questo bloccante: se non arrivano dati entro 5 secondi viene sollevata una timeout exception) fino al carattere di “a capo” (\r), ricevuto il quale possiamo visualizzare la temperatura ricevuta.
Tips
Il sorgente completo del programma è disponibile su Github; prima di passare allo sketch Arduino ecco un paio di suggerimenti:
- utilizzando diversi thread, è necessario prestare attenzione a come si aggiornano gli elementi della GUI: tali elementi infatti possono essere aggiornati solo dal thread che li ha creati. Da thread diversi, dobbiamo quindi chiamare metodi delegati del thread principale:
myForm.setTemp(message.Replace('.', ',')); [...] public delegate void setTempCallback(string temperature); public void setTemp(string temperature) { if (InvokeRequired) this.Invoke(new setTempCallback(setTemp), new object[] { temperature }); else { float floatTemp; |
- chiamare il metodo Abort() di un thread bloccato in Accept() non è sufficiente per chiudere tale thread, è necessario prima invocare il metodo Close() dell’oggetto Socket:
listeningSocket.Close(); serverThread.Abort(); log("Listening stopped"); |
Pagine: 1 2










Ciao Luca,
non riesco a trovare lo sketch puoi darmi maggiori indiaczioni? Puoi anche fornire l’eseguibile c#?
Grazie
ciao, trovi tutto nel Repository su GitHub.
Domanda, non ho capito a cosa serve:
>> Stash::prepare(PSTR(“$H”), sd);
?
ciao, serve a preparare il pacchetto che poi invierai con tcpSend(). Il contenuto del pacchetto è soltanto “sd”, ovvero l’oggetto stash creato prima che contiene la temperatura e il terminatore di riga \r. $H è un “segnaposto” per la variabile che specifichi dopo (“sd”). Se guardi gli altri esempi vedrai pacchetti più complessi, con una parte fissa (“GET…”) e alcune variabili ($H, $F…).