Introdução
Temos visto nos últimos anos uma crescente em computadores embarcados com conectividade Wi-Fi e Bluetooth. Podemos encontrá-los tanto no mundo maker e educacional como industrial. Um exemplo da indústria é a Toradex que no início deste ano anunciou seu primeiro módulo Colibri com conectividade Wi-Fi e Bluetooth.
Já os amantes do mundo maker e educacional ficaram muito animados no início deste ano, com a chegada do computador Raspberry Pi Zero W, que trouxe a mesma possibilidade de conexão usando Wi-Fi e Bluetooth, de sua irmã maior, a Raspberry Pi 3.
Este artigo tem por objetivo mostrar como tomar proveito da conectividade wireless da Raspberry Pi Zero W para desenvolver um pequeno robô que, além de ser controlado via aplicativo de smartphone, faz streaming de vídeo em tempo real para a tela do aplicativo. Veremos conceitos sobre Webserver com Node.js, troca de dados cliente/servidor em tempo real com socket.io, streaming com MJPG-streamer dentre outros temas.

Estrutura Mecânica do Robô
O robô foi montado utilizando uma estrutura da Pololu. É um chassi tipo esteira de tamanho compacto e com possibilidade de customização. É feito de plástico ABS, possui compartimento para 4 pilhas AA e furação para fixação de diversos tipos de shield e acessórios.

Foram utilizados dois motores DC, que encaixam perfeitamente no chassi, com redução em engrenagens de metal. Funcionam com até 12VDC.

Para acomodar a eletrônica e outros componentes, foi projetada uma estrutura no software Fusion 360 e impressa em 3D.

Principais Componentes Eletrônicos
Raspberry Pi Zero W
A placa principal, o cérebro do robô, consiste de uma Raspberry Pi zero W com processador Cortex ARM e conectividade Wi-Fi e Bluetooth. Por se tratar de uma placa que roda sistema operacional Linux, é possível desenvolver em C/C++, Python, Node.js dentre outras linguagens. Para este robô foi utilizado Node.js pois é uma linguagem que facilita o uso de recursos Web.

Driver Motor L293D Hat para Raspberry Pi
Uma placa desenvolvida especialmente para ser usada juntamente com os modelos da Raspberry Pi 2, 3 e Zero. É possível controlar até 4 motores DC. É um projeto open hardware e você pode apoiá-lo contribuindo no GitHub ou adquirindo uma unidade aqui.

Veja o manual da placa disponível aqui, com instruções de montagem, funcionamento e exemplos de programação em Python, C e Node.js.
Câmera Raspberry Pi
Foi utilizado um módulo de câmera genérico mas que tem o mesmo conector para Raspberry Pi. Com essa câmera é possível tirar fotos e gravar vídeo.

Funcionamento do sistema

- Aplicativo Web no Smartphone
O usuário utiliza um aplicativo Web no smartphone para o controle do robô. No aplicativo temos um joystick virtual com o qual se controla o robô enviando os comandos via WiFi. Além do joystick é possível visualizar o vídeo da câmera embarcada no robô em tempo real através do MJPG-Streamer. Esse aplicativo é escrito em HTML e javascript e é executado no navegador do smartphone.
- MJPG-Streamer
MJPG-streamer faz streaming de vídeo da câmera, através do protocolo HTTP, para a aplicação web no navegador do celular. O streaming poderá ser visto em qualquer navegador acessando o IP da Raspberry Pi.
- Web server Node.js executando na Raspberry Pi
Um web server escrito em Node.js é responsável por “servir” a aplicação Web ao cliente(navegador web no smartphone). É responsável também por receber os comandos do aplicativo do celular e convertê-los em comandos para o driver de motor. Essa troca de dados é feita em tempo real usando socket.io.
- Driver motor L293D para Raspberry Pi
A Raspberry Pi envia os comandos para determinados pinos GPIO que por sua vez controlam os motores através do driver L293D.
A seguir podemos ver mais detalhadamente o funcionamento de cada parte do sistema.
Aplicativo Web
A aplicação web executada no smartphone é escrita em HTML e javascript. A aplicação exibe um streaming de vídeo em tela cheia e também um joystick virtual para controle do robô. Veja abaixo o código completo do arquivo index.html da aplicação web.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Berry-E Robot</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
<link rel="manifest" href="manifest.json">
<script src="nipplejs/dist/nipplejs.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
</head>
<body>
<div id="dynamic" style="position: absolute; width:100%; height:100%">
<img id="video" width="100%" height="100%" src="https://192.168.0.55:8080/?action=stream" style="position: fixed;">
</div>
<script>
var socket = io();
var dynamic = nipplejs.create({
zone: document.getElementById('dynamic'),
color: 'black',
size: 200
});
dynamic.on('plain:up plain:down plain:left plain:right', function (evt, data) {
socket.emit('joystickData', data.direction.angle);
}).on('end', function (evt, data) {
socket.emit('joystickReleased',evt.type);
});
</script>
</body>
</html>
O vídeo é carregado através de uma tag <img> buscando o streaming de vídeo no IP da Raspberry Pi.
" ><img id="video" width="100%" height="100%" src="https://192.168.0.55:8080/?action=stream" style="position: fixed;">
O josytick utilizado foi o nippleJS. É um joystick virtual escrito em javascript que pode ser usado em telas touch. No trecho seguinte de código é declarado o joystick do tipo dinâmico, na cor preta e de tamanho 200.
var dynamic = nipplejs.create({
zone: document.getElementById('dynamic'),
color: 'black',
size: 200
});
Em seguida monitoramos os eventos up, down, left e right e end. Os dados de posicionamento do joystick são enviados ao servidor em tempo real usando socket.io através de socket.emit. Esses eventos serão tratados posteriormente pelo servidor Node.js.
dynamic.on('plain:up plain:down plain:left plain:right', function (evt, data) {
socket.emit('joystickData', data.direction.angle);
}).on('end', function (evt, data) {
socket.emit('joystickReleased',evt.type);
});

O nippleJS pode ser customizado de muitas formas. Veja sua documentação completa aqui.
Para que a aplicação tenha um “feeling” de aplicação nativa, foi utilizado um arquivo chamado manifest.json. Com este arquivo é possível definir um nome a aplicação, ícone, modo de exibição na tela e etc. O manifest.json também fornece a capacidade de salvar um site marcado como favorito na tela inicial de um dispositivo. Mais informações sobre essa técnica aqui.
{
"short_name": "Berry-E Robot",
"name": "Berry-E Robot",
"icons": [
{
"src": "/images/robot-icon.png",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "/index.html",
"display": "fullscreen",
"orientation": "landscape"
}


MJPG-Streamer na Raspberry Pi
A instalação do MJPG-streamer na Raspberry Pi é bem simples. Os passos são mostrados a seguir.
Clone o repositório em uma pasta do sistema:
cd /home/pi git clone https://github.com/jacksonliam/mjpg-streamer.git
Instale os seguintes programas necessários para compilação do MJPG-streamer.
sudo apt-get install cmake libjpeg8-dev
Então inicie a compilação.
cd mjpg-streamer/mjpg-streamer-experimental make sudo make install
Após a compilação você deve notar que temos um binário chamado mjpg_streamer e também alguns plugins .so dentre eles o input_raspicam.so e output_http.so.
Antes de executar o MJPG-streamer, certifique-se de que sua Raspberry está conectada a uma rede Wi-Fi e que a interface de câmera está configurada no raspi-config. Para executar o MJPG-streamer com a câmera da Raspberry Pi utilize os comandos abaixo.
export LD_LIBRARY_PATH=. ./mjpg_streamer -o "output_http.so -w ./www" -i "input_raspicam.so"
Se tudo ocorrer bem você poderá acessar o stream na URL seguinte:
https://<IP-raspberry>:8080/?action=stream
Note que usamos a mesma URL no código HTML da aplicação web.
Web Server Node.js
O programa escrito em Node.js utiliza o Express para “servir” o arquivo index.html, que é a nossa aplicação web, para qualquer cliente que se conecte no IP da Raspberry Pi. Assim que os dados de posicionamento do joystick começam a ser enviados do lado cliente, o servidor recebe esses dados e os converte para controlar o driver de motor. Essa comunicação é feita em tempo real utilizando socket.io. Veja o código completo do servidor abaixo:
var express = require('express');
var app = express();
var path = require('path');
var server = require('http').Server(app);
var io = require('socket.io')(server);
var rpio = require('rpio');
app.use(express.static(path.join(__dirname, '/public')));
// declaracao dos GPIOs motor A
const M1A = 31
const M1B = 15
const M1EN = 11
// declaracao dos GPIOs motor B
const M2A = 18
const M2B = 13
const M2EN = 29
// configuracao dos GPIOs como saida
rpio.open(M1A, rpio.OUTPUT, rpio.LOW);
rpio.open(M1B, rpio.OUTPUT, rpio.LOW);
rpio.open(M1EN, rpio.OUTPUT, rpio.LOW);
rpio.open(M2A, rpio.OUTPUT, rpio.LOW);
rpio.open(M2B, rpio.OUTPUT, rpio.LOW);
rpio.open(M2EN, rpio.OUTPUT, rpio.LOW);
// declaracao das funcoes de movimentacao frente, tras, esquerda, direita
function goForward() {
rpio.write(M1A, rpio.LOW);
rpio.write(M1B, rpio.HIGH);
rpio.write(M2A, rpio.HIGH);
rpio.write(M2B, rpio.LOW);
}
function goBackward() {
rpio.write(M1A, rpio.HIGH);
rpio.write(M1B, rpio.LOW);
rpio.write(M2A, rpio.LOW);
rpio.write(M2B, rpio.HIGH);
}
function goLeft() {
rpio.write(M1A, rpio.HIGH);
rpio.write(M1B, rpio.LOW);
rpio.write(M2A, rpio.HIGH);
rpio.write(M2B, rpio.LOW);
}
function goRight() {
rpio.write(M1A, rpio.LOW);
rpio.write(M1B, rpio.HIGH);
rpio.write(M2A, rpio.LOW);
rpio.write(M2B, rpio.HIGH);
}
function enableMotors() {
rpio.write(M1EN, rpio.HIGH);
rpio.write(M2EN, rpio.HIGH);
}
function disableMotors() {
rpio.write(M1EN, rpio.LOW);
rpio.write(M2EN, rpio.LOW);
}
io.on('connection', function(socket){
console.log('a user connected');
socket.on('disconnect', function(){
console.log('user disconnected');
disableMotors();
});
socket.on('joystickData', function(joystickData){
console.log(joystickData);
enableMotors();
if(joystickData == 'up') goForward();
if(joystickData == 'down') goBackward();
if(joystickData == 'left') goLeft();
if(joystickData == 'right') goRight();
});
socket.on('joystickReleased', function(){
console.log('Stop');
disableMotors();
});
});
server.listen(3000, '192.168.0.55', function () {
var port = server.address().port
console.log('Server listening:%s...', port);
});
O servidor monitora o evento joystickData. Quando os dados vão sendo recebidos, a função de movimentação correspondente é chamada.
socket.on('joystickData', function(joystickData){
console.log(joystickData);
enableMotors();
if(joystickData == 'up') goForward();
if(joystickData == 'down') goBackward();
if(joystickData == 'left') goLeft();
if(joystickData == 'right') goRight();
});
Para controle de GPIOs através de Node.js, foi utilizado pacote chamado rpio. Mais detalhes aqui.
Driver Motor L293D para Raspberry Pi
O driver desenvolvido pode controlar até 4 motores mas para este projeto foram utilizados apenas 2 motores. O driver tem o mesmo fator de forma da Raspberry Pi Zero e segue a pinagem abaixo:

São utilizado 3 pinos de controle para cada motor. Dois pinos indicam o sentido e 1 pino habilita/desabilita o movimento.
Por exemplo, na seguinte função em Node.js, ativa-se um motor no sentido horário e outro anti-horário, fazendo com que o robô se movimente para frente.
function goForward() {
rpio.write(M1A, rpio.LOW);
rpio.write(M1B, rpio.HIGH);
rpio.write(M2A, rpio.HIGH);
rpio.write(M2B, rpio.LOW);
}
Para saber mais sobre este driver de motor, o articulista do Embarcados Pedro Bertoleti, fez um super review em vídeo, explicando vários pontos técnicos dessa placa. Confira o vídeo na íntegra aqui.
Conclusão
Com os vários conceitos mostrados neste artigo, foi possível desenvolver um pequeno robô controlado pelo celular tomando proveito da conectividade Wi-Fi da placa Raspberry Pi Zero W. Utilizando-se de recursos web tanto do lado cliente como servidor foi possível desenvolver um aplicativo web para smartphone que contém um joystick virtual e exibe streaming de vídeo em tempo real.
Veja um vídeo do robô em funcionamento:
O código fonte de toda aplicação pode ser encontrado no GitHub.
Referências
Express – https://expressjs.com/
Socket.io – https://socket.io/
NippleJS – https://yoannmoinet.github.io/nipplejs/
node-rpio – https://github.com/jperkin/node-rpio
Aplicativos web com Manifest.json
MJPG-Streamer – https://github.com/jacksonliam/mjpg-streamer









Olá, qual seria o link do código do github? O código está todo aqui no artigo, mas eu gostaria de olhar o arquivo package.json.
Olá Rafael!
Fiz update do link do GitHub no final do artigo.
https://github.com/giobauermeister/berryE-raspberrypi-robot
Coloquei lá todo o código e também o package.json.
Obrigado pela leitura!
Parabéns Giovanni, excelente projeto!
Obrigado Miller!