Nous allons nous concentrer aujourd’hui sur la réalisation de la communication du Raspberry Pi du PiRobot avec le reste du réseau. On pourra ainsi contrôler le PiRobot via le réseau ou via un Wifi Direct que l’on mettra en place plus tard. Pour se faire nous allons passer par le protocole UDP afin de discuter avec le PiRobot. Dans cette partie, nous verrons pourquoi j’utilise UDP au lieu d’un autre protocole et aussi comment j’ai mis en place et testé le serveur.

 

 

1. Pourquoi privilégier une transmission UDP ?

1.1. Qu’est-ce que UDP ?

UDP (User Datagram Protocol) est un des principaux protocoles de communication utilisés sur Internet. UDP est un protocole utilisant la couche Ethernet et IP du modèle OSI. Pour notre application, ce protocole permet de se connecter via un réseau filaire Ethernet ou sans fil Wifi au PiRobot. Pour se faire, le PiRobot doit pouvoir accepter ce type de communication, d’où la nécessité de réaliser un serveur UDP sur le Raspberry Pi du PiRobot.

Le rôle de ce protocole est de permettre la transmission de données de manière très simple entre deux entités, chacune étant définie par une adresse IP et un numéro de port. UDP est très utile pour transmettre rapidement de très petites quantités de données entre une ou plusieurs machines clients et un serveur. Ce protocole est aussi adapté aux données pour lesquelles la perte d’un paquet n’est pas importante, ce qui sera notre cas, vu que la communication sera constante et que notre client enverra un flux continu de données au PiRobot.

Le domaine de la voix sur IP, des jeux vidéo en lignes ou du streaming sont des utilisateurs typiques de ce protocole.

1.2. Pourquoi privilégier ce protocole ?

Lorsqu’on veut discuter rapidement sur IP, deux protocoles sont majoritaires : TCP et UDP. Ce sont des protocoles reconnus par POSIX et contenus dans les bibliothèques standards. Ces deux protocoles utilisent des « sockets ».  J’ai choisi UDP pour 3 raisons :

  • Légèreté de la trame transmise
  • Rapidité de transmission (temps-réel privilégié sur la fiabilité)
  • Pas de négociation client/serveur

Les trames TCP et UDP sont encapsulées dans un paquet IP :

Voici l’entête d’un datagramme UDP :

Et voici la structure d’un segment TCP :

C’est un peu plus compliqué en TCP… De plus qu’une session TCP fonctionne en trois phases :

  • Etablissement de la connexion (handshaking en 3 temps)
  • Transfert de données (chaque transfert demande un accusé de réception de la cible)
  • Fin de connexion (handshaking en 4 temps)

En bref, le TCP est très fiable, mais trop lent pour une utilisation quasi temps-réel. Le protocole UDP est donc largement privilégié. Et étant en wifi direct ou via mon réseau local, les pertes de paquets UDP seront très faible dans ce projet.

2. Côté PiRobot – Codons notre serveur UDP !

Ci-dessous, le code permettant d’établir une liaison UDP dans notre programme. J’ai choisi le port 26000 sur le PiRobot. Ce sera notre port d’écoute.

La boucle while(1) est notre boucle de réception. Cette boucle sera en attente de message à la méthode « recvfrom ». Dès que le message arrivera, il sera récupéré dans la variable « buf ». Un message de retour sera transmis grâce à la méthode « sento ».

Serveur UDP C/C++
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <netdb.h>


void error(const char *msg);

int main(void)
{

	std::cout << "Lancement du serveur UDP..." << std::endl;

	int sock, length, n;
	socklen_t fromlen;
	struct sockaddr_in server;
	struct sockaddr_in from;
	char buf[1024];

	sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock < 0) error("Opening socket");
	length = sizeof(server);
	bzero(&server, length);
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = INADDR_ANY;
	server.sin_port = htons(26000);
	if (bind(sock, (struct sockaddr *)&server, length) < 0)
		error("binding");
	fromlen = sizeof(struct sockaddr_in);
	
	std::cout << "Serveur UDP en écoute sur le port 26000 !" << std::endl;
	
	while (1) 
	{
		n = recvfrom(sock, buf, 1024, 0, (struct sockaddr *)&from, &fromlen);
		if (n < 0) error("recvfrom");
		write(1, buf, n);
		n = sendto(sock, "Etat PiRobot", 17,
			0, (struct sockaddr *)&from, fromlen);
		if (n < 0) error("sendto");
	}


		return 0;
}



void error(const char *msg)
{
	perror(msg);
	exit(0);
}

Ce code peut aussi être inséré dans un thread qui sera appelé en parallèle du main thread. Je traiterai ce sujet lors du prochain article !

3. Côté Client – Testons notre serveur avec Packet Sender !

Afin de tester l’application sur le PiRobot, j’utilise Packet Sender sur le PC de développement. Ce petit soft qui permet d’envoyer des trame TCP et/ou UDP est utilisable sur toutes les plateformes (Linux, Windows…) et est téléchargeable ici : https://packetsender.com/

Packet Sender

Packet Sender

Dans la ligne ASCII, je tape le message que je veux transmettre. Lorsqu’on envoi le message, on a le retour du PiRobot « Etat PiRobot ». C’est le retour d’information du serveur qui me servira à l’avenir à renvoyer les informations du robot : couple moteur, retour caméra…

Sur la dernière monture de Visual Studio (Visual Studio 2017 RC), il est possible d’avoir un retour de la console du système embarqué distant en mode Debug. Ici s’affiche le retour console de l’application serveur. On peut y voir les différents tests que j’ai effectué.

Console distante SSH de Microsoft Visual Studio 2017 RC

Console distante SSH de Microsoft Visual Studio 2017 RC

Dans le prochain article, j’aborderai la manière dont j’ai arrangé le code avec les différentes exécutions parallèles. Afin d’avoir d’un côté le serveur UDP et de l’autre la gestion hardware du robot.

 

BenTeK.

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

Recevez les bons outils pour débuter dans l'univers DIY et l'impression 3D !