Olá leitores! Como primeiro artigo, pretendo mostrar como fazer uma comunicação TCP/IP via ethernet, enviando uma string simples de um PC para uma Raspberry Pi 2 rodando com o sistema operacional Windows 10 IoT Core. No desenvolvimento, foi utilizado linguagem C# com o UWP (Universal Windows Plataform).
UWP – Universal Windows Plataform
UWP é um tipo de projeto da Microsoft que permite criar aplicações que rodem em todas as plataformas Windows (Windows Phone, Xbox, Windows 1o IoT, Windows 8 e superior, etc), sem a necessidade de alterar o código. Com isso, vieram novos conceitos, pelo menos para quem está acostumado a trabalhar com Windows Form como eu. De questões de design, até a necessidade de declarar que a sua aplicação será um server, uma boa mudança foi o fato de que boa parte das coisas dos métodos de um aplicação usando UWP são assíncronos e, além disso, outra grande mudança é que a parte dos namespace mudaram para algo no formato Windows.Alguma.Coisa, melhorando assim a curva de aprendizado.
Além disso, não é possível “criar” uma Thread (neste novo formato, não existe classe Thread). Para isso, é necessário fazer o método como assíncrono ou Task, permitindo a criação de tarefas executando paralelamente.
Mais informações sobre UWP podem ser encontradas neste link.
Windows 10 IoT Core
O Windows 10 IoT Core é a versão do Windows 10 que foi otimizada para pequenos dispositivos. Ela é a tentativa da Microsoft de levar o Windows 10 para o segmento de embarcados. Até o momento da escrita deste artigo, há suporte para os seguintes dispositivos / SBC:
- Raspberry Pi 2;
- Arrow DragonBoard 410c;
- MinnowBoard Max.
O Windows 10 IoT Core é bem pesado levando em conta o mundo dos sistemas embarcados, Os recursos necessários para sua execução são:
- 256MB RAM (128MB livres para o OS) para dispositivos sem display (headless);
- 512MB RAM (256MB livres para o OS) para dispositivos com display;
- Processador com clock 400MHz (x86 com PAE, NX e SSE2);
- 2GB de armazenamento.
Ao contrário do que se espera em sistemas operacionais mais voltados a melhoria de experiência de uso, na minha experiência com esta versão do Windows, eu achei que há pouco suporte a hardware / drivers de dispositivos terceiros. Eu tive problemas com a placa WiFi, onde não consegui fazer funcionar dois modelos distintos.
Portanto, antes de iniciar o desenvolvimento com este sistema operacional, recomendo consultar este site para maiores informações de quais hardwares terceiros são suportados.
Uma vantagem do Windows em relação ao Linux, na pouca experiência que eu tive com o Linux embarcado, é que é possível gerenciar a placa através do browser. Este recurso é chamado de Windows Device Portal. Eu pessoalmente nunca vi algo parecido no Linux. Por exemplo, é possível verificar o uso de memória RAM ou de processamento, tudo via browser. Segue abaixo uma imagem do Windows Device Portal. No site Windows Device Portal há mais informações:
Projeto
O projeto deste artigo são, na verdade, dois projetos em uma Solution só: um para ser o Server e outro o Client. Neste artigo será utilizado o UWP no PC com a linguagem C# para estabelecer a comunicação TCP com a Raspberry Pi com Windows 10 IoT Core. O projeto pode ser encontrado no meu GitHub.
Caso queriam ver com maiores detalhes as classes e rodar, há uma dica: para rodar tanto o Client como o Server abra 2 instâncias de Visual Studio (VS), pois quando for compilado o código e estiver rodando em uma das plataformas não será possível fazer o build de outra arquitetura, ou seja, por enquanto não é possível fazer o debug de um código em x86 e ARM ao mesmo tempo. Pode ser que no futuro a Microsoft consiga mudar isso.
- ServerSocket (Server)
- StreamSocketUniversalApp (Client)
Requisitos
- Visual Studio 2015;
- SDK do Windows 10 IoT.
Diagramas
Enviando\Recebendo uma String
Primeiro irá ser apresentado como receber uma string e depois como enviar
Recebendo
Essa parte roda na Raspberry Pi 2. Classe SocketServer é responsável por ficar ouvindo uma porta específica.
Os namespace necessários para o funcionamento da classe SocketServer:
//Os using necessario para o funcionamento das classes using System; using Windows.Networking.Sockets; using Windows.Storage.Streams;
Classe SocketServer:
private readonly int _port;
public int Port { get { return _port; } }
private StreamSocketListener listener;
public delegate void DataRecived(string data);
public event DataRecived OnDataRecived;
public delegate void Error(string message);
public event Error OnError;
As variáveis da classe SockeServer:
- Port: Porta que o server irá escutar;
- Listener: Classe que fica escutando;
- Delegate DataRecived: Assinatura de método para quando receber a string;
- Event OnDataRecived: Evento disparado quando recebe uma string;
- Delegate Error: Assinatura de método em caso de erro.
- Event OnError: Evento disparado quando acontece algum erro.
public async void Star()
{
try
{
...
//Criar uma nova instancia do listerner
listener = new StreamSocketListener();
//Adiciona o evento de conexão recebida ao método Listener_ConnectionReceived
listener.ConnectionReceived += Listener_ConnectionReceived;
//Espera fazer o bind da porta
await listener.BindServiceNameAsync(Port.ToString());
}
catch (Exception e)
{
//Caso aconteça um erro, dispara o evento de erro
if (OnError != null)
OnError(e.Message);
}
}
Como foi dito anteriormente, é utilizado bastante métodos assíncronos, a keyword async no método faz com que ele seja assíncrono, e a keyword await força a ficar naquele ponto até que o método seja completado. No método de conexão, é o await que espera fazer o bind de uma porta, sendo o uso do try catch necessário pois, caso aconteça um erro, como por exemplo esquecer de dar permissão para que aplicação seja um server, o sistema operacional é capaz de informar o erro e não dar crash na aplicação.
private async void Listener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
var reader = new DataReader(args.Socket.InputStream);
try
{
while (true)
{
//Espera chegar um dado e verifica se está conectado ou não
uint sizeFieldCount = await reader.LoadAsync(sizeof(uint));
//Caso ocora um desconexão
if (sizeFieldCount != sizeof(uint))
return;
//Tamanho da string
uint stringLenght = reader.ReadUInt32();
//Ler os dados do InputStream
uint actualStringLength = await reader.LoadAsync(stringLenght);
//Caso ocora um desconexão
if (stringLenght != actualStringLength)
return;
if (OnDataRecived != null)
{
//Le a string com o tamanho passado
string data = reader.ReadString(actualStringLength);
//Dispara evento de dado recebido
OnDataRecived(data);
}
}
}
catch (Exception ex)
{
//Dispara evento em caso de erro, com a mensagem de erro
if (OnError != null)
OnError(ex.Message);
}
}
A keyword var resolve o tipo de variável durante a compilação, como a keyword auto em C++.
Na classe principal, chamada StartupTask, é feita uma aplicação sem interface gráfica / headless. Ela possui o método Run que é equivalente ao método main de outras aplicações.
using Windows.ApplicationModel.Background;
using Windows.System.Threading;
// The Background Application template is documented at https://go.microsoft.com/fwlink/?LinkID=533884&clcid=0x409
namespace ServerSocket
{
public sealed class StartupTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
//
// TODO: Insert code to perform background work
//
// If you start any asynchronous methods here, prevent the task
// from closing prematurely by using BackgroundTaskDeferral as
// described in https://aka.ms/backgroundtaskdeferral
//
taskInstance.GetDeferral();
var socket = new SocketServer(9000);
ThreadPool.RunAsync(x => {
socket.Star();
//Assinatura de eventos
socket.OnError += socket_OnError;
socket.OnDataRecived += Socket_OnDataRecived;
});
}
private void Socket_OnDataRecived(string data)
{
}
private void socket_OnError(string message)
{
}
}
}
E por último e o mais importante: não declarar que ele é um Client & Server no manifest vai fazer com que a aplicação não funcione.
Enviando a string:
O Cliente foi testado em um Windows 10 usando o UWP. O teste não foi feito usando outra plataforma Windows e também não foi seguindo as diretrizes de design para o UWP (a interface do client foi feita de maneira bem simples).
Classe SocketClient:
private readonly string _ip; private readonly int _port; private StreamSocket _socket; private DataWriter _writer; private DataReader _reader; public delegate void Error(string message); public event Error OnError; public delegate void DataRecived(string data); public event DataRecived OnDataRecived;
A keyword readonly parece com uma const, a diferença é que a const é resolvida em tempo de compilação e o readonly em tempo de execução. No construtor da classe é passado a porta e o Ip que ele irá se conectar. As variáveis da classe são:
- _port: Porta que irá se conectar;
- _ip: Ip para conexão;
- _socket: Socket para fazer a conexão;
- _writer: Para enviar a mensagem;
- _reader: Para ler o que foi enviado;
- Delegate DataRecived: Assinatura de método para quando receber a string;
- Event OnDataRecived: Evento disparado quando recebe uma string;
- Delegate Error: Assinatura de método em caso de erro;
- Event OnError: Evento disparado acontece algum erro.
public async void Connect()
{
try
{
var hostName = new HostName(Ip);
_socket = new StreamSocket();
await _socket.ConnectAsync(hostName, Port.ToString());
_writer = new DataWriter(_socket.OutputStream);
Read();
}
catch (Exception ex)
{
if (OnError != null)
OnError(ex.Message);
}
}
O método acima cria a conexão com um server.
public async void Send(string message)
{
//Envia o tamanho da string
_writer.WriteUInt32(_writer.MeasureString(message));
//Envia a string
_writer.WriteString(message);
try
{
//Faz o Envio da mensagem
await _writer.StoreAsync();
//Limpa para o proximo envio de mensagem
await _writer.FlushAsync();
}
catch (Exception ex)
{
if (OnError != null)
OnError(ex.Message);
}
}
Uma vez estabelecida uma conexão é possível enviar e receber mensagens. O método acima serve para fazer o envio da mensagem, os métodos WriteUInt32 e WriteString escreve no buffer de envio, o método StoreAsync é quem faz o envio efetivamente do que está no buffer e o Flush limpa o buffer, e o recebimento é mesmo método que foi feito na Raspberry Pi 2.
public void Close()
{
_writer.DetachStream();
_writer.Dispose();
_reader.DetachStream();
_reader.Dispose();
_socket.Dispose();
}
Por último e não menos importante: no método que fecha uma conexão, o DetachStream serve para desassociar um stream e o Dispose serve para avisar para o GarbageColletor que aquela instância daquela classe pode morrer, liberando assim recursos do sistema.
Agora é mostrado o código da tela e depois a tela em si. A tela só precisa de uma variável:
SocketClient _socket;
private void btnConnect_Click(object sender, RoutedEventArgs e)
{
if(_socket != null)
_socket = null;
_socket = new SocketClient(txtIp.Text, Convert.ToInt32(txtPort.Text));
_socket.Connect();
}
private void btnSend_Click(object sender, RoutedEventArgs e)
{
_socket.Send(txbMessage.Text);
}
Os métodos acima fazem a conexão e o envio de uma mensagem.
Segue uma imagem de como ficou a tela.
Observação: os números que aparecem na tela são porquê eu estou utilizando o modo de desenvolvedor do Windows 10.
Conclusão
Neste artigo foi apresentada uma forma de fazer comunicação TCP/IP entre um PC com Windows 10 e uma Raspberry Pi 2 com a plataforma Windows 10 IoT Core enviando uma string, com a utilização da linguagem C#, tanto na Raspberry quanto no PC. No projeto a Raspberry Pi estava conecta via ethernet e o PC no Wi-Fi.
O envio e recebimento de uma string não exige um código complexo e é possível fazer isso com poucas linhas de código. Uma dificuldade que tive foi de encontrar informações sobre o UWP, pois é um tipo de projeto relativamente novo e existe poucas pessoas desenvolvendo com ele, então tirar uma dúvida sobre código demora um pouco mais que o normal. O lado bom é que a documentação da Microsoft é boa e ela disponibilizou um vasto número de projetos no Github.
Uma outra dificuldade é a curva de aprendizado, pois para quem já está acostumado com Windows Forms, é quase como ter que aprender um novo Framework, porém algumas coisas ficaram mais fáceis, como por exemplo fazer um criptografia.
Nos próximos artigos pretendo mostrar como fazer o envio de um array de bytes, o código é um pouco diferente desse código.
Referências
Fonte da imagem destacada: https://www.iwillfolo.com/raspberry-pi-2-will-run-windows-10/









posso enviar mensagens de outras plataforma como web ou android utilizando esse server em UWP?
o envio das mensagens seria possível ser realizado em outras plataforma como web ou android?
Ótimo tutorial Rafael, parabéns! Não sou apaixonado pela MS, mas não achei o Win10IoT tão ruim assim no RaspPi2, tão pouco absurdamente lento. Comparando o boot, por exemplo, do Raspbian Jesse (interface completa), este ganhou por alguns segundos. O grande ponto positivo da MS é de fato a interface web, funcionando perfeitamente como um painel de controles. As bibliotecas VC# pro “baixo nível” (GPIOs, SPI, …) também estão bem completas e documentadas. Enfim, a MS “começou agora” nos embarcados, e na minha opinião, começou bem. Precisa melhorar, claro, mas está no caminho certo.
Valeu pelas informações e o tutorial Rafael mas desculpe , só sendo muito fã da MS , ou só sabendo lidar com o ambiente dela ou ainda “não tenho como evitar, o cliente quer pagar por isso” para aturar o WIndows 10 na RasPi. Testei uma vez e retornei imediatamente para o Linux. Não consigo lembrar de outra coisa do Windows 10 IoT senão Lento, pesado, limitado, chato e altamente dependente de um PC. A raspi com Linux é totalmente autônoma só para começar, depois tem … Python, sem comparação. A MS tem que evoluir muito se quiser competir nesse… Leia mais »
Fabio, concordo em alguns ponto com vc, que o Windows 10 IoT é pesado e quando vc desenvolve com UWP a sua aplicação vai possuir algumas limitações bem chatas e que o Windows 10 IoT precisa melhorar na questão de suporte a hardware mas não acho que ela seja dependente do PC. Mas se eu fosse fazer uma aplicação comercial com Raspberry eu iria usar linux. Vc tem algum problema com Win10IoT ? Além da performace e que parte do Windows 10 IoT vc achou altamente dependente do PC?
Parabéns. Ótimo, Gostaria de saber se é possível o envio de vários arquivos para aplicação servidora. Assim como na classe System.Net
Ou seja o envio de um Array.
Obrigado.