Da Arduino a C# via socket

by luca
27 comments

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:

[checklist]

  • nella prima, vedremo come sviluppare una applicazione socket in C#
  • nella seconda, vedremo come scrivere uno sketch per Arduino

[/checklist]

Socket server in C#

Il framework .Net mette consente di operare sui socket in due modalità: sincronaasincrona.

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");

Related Posts

27 comments

lucadentella.it – Da Arduino a C# via socket | scatol8® | Scoop.it 1 febbraio 2013 - 20:35

[…] 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 …  […]

Reply
pixel 14 marzo 2013 - 15:23

Ciao Luca,
non riesco a trovare lo sketch puoi darmi maggiori indiaczioni? Puoi anche fornire l’eseguibile c#?

Grazie

Reply
luca 14 marzo 2013 - 21:18

ciao, trovi tutto nel Repository su GitHub.

Reply
lucadentella.it – enc28J60 e Arduino (14) 22 aprile 2013 - 19:27

[…] Nell’esempio, leggerò con Arduino la temperatura dell’ambiente tramite il sensore DS18B20 di cui vi ho già parlato in questo articolo. […]

Reply
igor 3 maggio 2013 - 15:28

Domanda, non ho capito a cosa serve:
>> Stash::prepare(PSTR(“$H”), sd);
?

Reply
luca 3 maggio 2013 - 20:45

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…).

Reply
wolf 25 settembre 2013 - 03:09

Hi Luca.
nice tutorial, do the job very well.
I’m write the PC side (your C#) in Perl. the work is the same, the look and feel not.

Reading your tutorial, analizing the TCP packages, working with my perl socket script, I can see how the TCP port open and close for every temperature send by the arduino.

It is possible (I don’t know how) open permanently the port and send the text over and over the same open port?

It is possible open the port and do something bidirectinally (receive and send data over TCP to the arduino)

anyway, Thanks a lot for every tutorial in our webpage. it’s rocks

Reply
mourinhothinh 14 ottobre 2013 - 03:26

hi Luca
I had download you code. I replace the sensor data by code: float float_temp = 27.5; because i don’t have the sensor and the library.
But when i build the code in arduino and C#, nothing happen. it’s not connect between the arduino and pc.
Can you help me?

Reply
luca 23 ottobre 2013 - 21:40

Hi, are you sure the IP address of Arduino and of your PC (running the demo app) are in the same network? Are you able to ping the Arduino address from your PC?

Reply
mourinhothinh 20 ottobre 2013 - 10:59

hi Luca,

I want to send data from C# to arduino. i try function :
ether.tcpReply() but the arduino don’t receive anything. can you help me?

if you have any example can you post it to your website?
thank so much!

Reply
luca 23 ottobre 2013 - 21:36

Hello!

you use the tcpReply() to send data FROM Arduino… to receive data (sent from a C# app) to Arduino you can follow the examples as it was a webserver, like this one.

Reply
Maximiliano El,izondo 26 ottobre 2013 - 15:25

Hi! Nice work! 😉
I am working with Arduino and C# but I need to work in a asyncronous way… do you have any example? Do you know if there is some web site or book to find some good information? Thank you very much!

Reply
luca 26 ottobre 2013 - 16:20

Hi Maximiliano,

what do you mean for “asyncronous” way? c# side, using the non-blocking socket methods?

Reply
Salinda 16 novembre 2013 - 17:54

Hi Luca i’m working on RFID with LAN, i downloaded your program, and c# code works fine when i test i with a c# client (on my LAN using different PC ), but my Arduino does not send any data, but i can ping to it, this setup is on LAN no internet is that a problem? Thanx in advance

Reply
luca 16 novembre 2013 - 19:53

Hi Salinda,

no, it doesn’t need an Internet access to work… did you use the exact Arduino sketch that is in my repository? Which ethernet shield are you using?

Reply
salinda 17 novembre 2013 - 03:14

no i change it litte, coz i dont have a dhcp so i use Static setup function, and for the float i give fixed value. my problem is pings works, but there is no coming data to c# , this is the shield i use
http://dereenigne.org/wp-content/uploads/ENC28J60.jpg

Reply
Salinda 17 novembre 2013 - 04:37

Hi Luca, i got it solved, it’s the Ether Card new Library, i use the old library form here https://github.com/vworp/OldEthercard

i test it with both dhcp and static works fine, new library is not working for both(at the moment),

can you please help me on how to send a data back, like Acknowledgmentt back to Arduino when i recive a data.(ex RFID recived and display a username on lcd)

Reply
luca 17 novembre 2013 - 10:39

Good! Very strange the new library doesn’t work… anyway I’m working on a bidirectional example!

Reply
mourinhothinh 20 novembre 2013 - 02:03

Hi luca,
I have a propblem. I send data from arduino to C# app. I use ether.tcpSend() function like you example. It work. But it just work about 5 minute or send about 32 times and then it doesn’t work. it don’t send data from arduino(ENC28j60) to C# app any more. I don’t know how?

thank so much!

Reply
mourinhothinh 20 novembre 2013 - 10:07

I think the buffer is full. can you tell me how to clear the buffer?

thanks!

Reply
rafael 8 luglio 2014 - 01:51

Hello Lucas. I am working on a project where the Arduino is a client and have to send and receive data received by the serial port to ethernet ENC28J60 like the example you have in C #. Could you please help me?

Reply
luca 8 luglio 2014 - 08:11

Hi Rafael, what help do you need?

Reply
rafael 8 luglio 2014 - 22:43

I just need to send and receive against a set of strings through an ethernet connection client, already tried several examples but none worked so they send the data but not receive anything.

Reply
Bogdan 5 agosto 2015 - 12:18

Can you please put libraries you used on github or just send them to my email. nmarbm@gmail.com. I im using W5100 ethernet shield and cant really just upload your sketch to arduino adn I dont really understand code that well to change your code to work on my shield.

Reply
luca 6 agosto 2015 - 12:27

Hi! The library I use for my shield is the Ethercard one from Jeelabs: https://github.com/jcw/ethercard

Reply
Fernando 4 settembre 2019 - 04:53

Buon pomeriggio, nel codice di c # non riesco a trovare dove configurare su quale porta il software PC ascolta. È il PC che funge da server e Arduino come client, giusto?

Reply
luca 20 settembre 2019 - 09:25

corretto, la porta è configurabile da GUI (nel mio esempio è la 1000)

Reply

Leave a Comment