Para um workshop onde ia falar sobre Arduino, tinha que mostrar um projeto mais interessante – então, nada como um robô controlado por Wifi.

Componentes usados

Se não sabem o que é o wifi-shield da Adafruit CC3000, podem consultar no Adafruit learning center, escrito por Rick Lesniak. Além de explicar como soldar, também explica as ligações e algumas das funções.

Para funcionar, não podem esquecer de efectuar o download das bibliotecas (podem consultar o github ou descarregar directamente a ultima versão) e adicionar ao vosso IDE do Arduino.

Após efectuarem o download e terem adicionado ao vosso IDE, podem executar os exemplos existentes.

Este tutorial para o Robô é baseado no existente no Adafruit Learning Center – WiFi controlled Mobile Robot – , escrito por Marco Schwartz. As diferenças é que o driver de motor que eu usei não é o mesmo, logo, há funções que no código não iriam funcionar, então, alterei e adicionei funções e instruções ao código que irei colocar aqui.

Podem sacar o codigo completo do projecto na minha página do bitbucket – arduino-wifi-robot

L298 Dual H-Bridge

L298

O driver de motores – L298 Dual H-Bridge – já o conheço e já anteriormente funcionei com ele, e este também pode usar PWM, mas só funciona a partir de um determinado valor do PWM (100), mas desta vez decidi não usar o PWM. Por causa disso e de outras funções, alterei grande parte do código que controla o movimento.

Ligações

Ligações necessárias no Wifi Shield CC3000

Wifi Shield CC3000 Arduino PINs
ADAFRUIT_CC3000_IRQ 3
ADAFRUIT_CC3000_VBAT 5
ADAFRUIT_CC3000_CS 10

Ligações para o L298 Dual H-Bridge

L298 Dual H-Bridge Arduino PINs
ENA 6
IN1 4
IN2 2
IN3 7
IN4 8
ENB 9
GND GND
+5V 5v

wifiRobotMotorConnections

O motor shield contêm ainda mais ligações, onde vamos ligar os motores – no caso da imagem em cima, Motor do lado esquerdo liguem em MOTORB e o do lado direito no MOTORA. Para este projeto, é necessário alimentar o motor shield com alimentação externa – Neste caso, liguem o GND ao GND e o VCC ONDE DIZ VMS .

Aqui fica um resumo das ligações e dos shields

RobotShields

 

RobotShieldsSide

 

Sketch

Podem encontrar o sketch na minha página do bitbucket. Irei apenas referir aqui as funções principais. Para saberem mais informações, irei colocar o atalho para a página da Adafruit onde é explicado esta parte do código.

Página da Adafruit onde podem ler mais informações sobre o código

Adicionar as bibliotecas necessárias

// Include required libraries
 #include <Adafruit_CC3000.h>
 #include <ccspi.h>
 #include <SPI.h>
 #include <string.h>
 #include "utility/debug.h"
 #include <stdlib.h>

O servidor vai enviar parâmetros para o robot. Neste caso, como não usamos aceleração (PWM), alterei um pouco a variável para receber os valores da direção : esquerda, direita, avançar, recuar .

Estes valores vão ser guardados na seguinte variável – que é um array de quatro elementos.

int motorCommand[4];

Definir os PINs para os motores

// Motor pins
int enableA = 6;
int enableB = 9;
int pinA1 = 4;
int pinA2 = 2;
int pinB1 = 7;
int pinB2 = 8;

Definir os vossos parâmetros de rede

// WiFi network (change with your settings !)
 #define WLAN_SSID "ssid_da_rede" // cannot be longer than 32 characters!
 #define WLAN_PASS "password"
 #define WLAN_SECURITY WLAN_SEC_WPA2 // This can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2

Definir o porto para escutar ligações

 #define LISTEN_PORT 8888

Criar uma instância do CC3000

Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT, SPI_CLOCK_DIV2);

//Criar o servidor

 Adafruit_CC3000_Server robotServer(LISTEN_PORT);

A parte de setup do nosso sketch.

void setup() {
 Serial.begin(9600);
 result = "";

Primeiro, precisamos definir o tipo de PINs dos motores

 pinMode (enableA, OUTPUT);
 pinMode (enableB, OUTPUT);
 pinMode(pinA1, OUTPUT);
 pinMode(pinA2, OUTPUT);
 pinMode(pinB1, OUTPUT);
 pinMode(pinB2, OUTPUT);

Iniciar a rede wifi e obter um endereço IP

/* Initialise the module */
 Serial.println(F("nInitializing..."));
 if (!cc3000.begin()) {
 Serial.println(F("Couldn't begin()! Check your wiring?"));
 while(1);
 }
if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
  Serial.println(F("Failed!"));
  while(1);
}
Serial.println(F("Connected!"));

Quando ligado, mostrar os detalhes da ligação (IP / DNS / Gateway)

Serial.println(F("Request DHCP"));
 while (!cc3000.checkDHCP()) {
 delay(100); // ToDo: Insert a DHCP timeout!
 }
/* Display the IP address DNS, Gateway, etc. */
   while (! displayConnectionDetails()) {
   delay(1000);
}

outputRobot
Detalhes da ligação – Guardem o IP – vão precisar

A ultima parte do setup é ficar à espera de ligações.

// Start listening for connections
robotServer.begin();
Serial.println(F("Listening for connections..."));

Extrair a informação que vem do client com a informação para os motores é realizada na função format_result. O resultado vem do cliente e a função separa e insere no array.

// Format result and extract the variables

format_result(motorCommand,result);

Apenas para nossa informação, imprimimos as informações.

Serial.println (String(motorCommand[0]) + ":" + String(motorCommand[1]) + ":" + String(motorCommand[2]) + ":" + String(motorCommand[3]));

O array vai apenas conter 0 e 1s – O que estiver a 1 é a direcção para onde desejamos ir: esquerda, direita, frente, trás

Assim que a nossa função format_result() terminar de processar, enviamos os resultados aos motores.

// Send motor commands
send_motor_command (motorCommand[0],motorCommand[1],motorCommand[2],motorCommand[3]);

A função dos motors, simplesmente vê onde está o valor 1 e avança conforme.

void send_motor_command(int left, int right, int fwd, int bwd) {
 
 //lets check what we have
 if (fwd) {
 forward();
 //delay(1000); //1s
 }
 
 if (bwd) {
 backward();
 //delay (500); // 1/2s
 }
 
 if (left) {
 turnLeft();
 }
 
 if (right) {
 turnRight();
 }
 
 //stop
 if ((!left) && (!right) && (!fwd) && (!bwd)) {
 stopRobot();
 }
}

As funções enable(), disable(), backward(), forward(), turnRight(), turnLeft() são as que dão movimento aos motores e consequentemente ao robô ! Estão programadas para este driver de motor – L298 Dual H-Bridge

Interface Web

A página principal chama-se robot.html e tem o seguinte aspeto:

RobotOffile

Adicionei uma pequena coisa que é o status da ligação ao robot. Assim que houver ligação, a bolinha vermelha fica verde !

Isto é uma pequena parte efetuada em AJAX que permite apenas saber quando já há ligação com o robô !

O ficheiro é pingPHP.php e a linha que tẽm que alterar é:

$host = "<endereco_ip>";

Quando então houver ligação, altera-se o icon:

RobotOnline

Aqui está a parte do código que controla o movimento.

As setas de direção controlam o movimento do robô ! Para qualquer direção, clicar na seta (imagem) correspondente –  O seguinte código – javascript – controla estas operações.

//set clicable images
 //forward
 $('#top').on('click', function () {
 fullForward();
 });

//Esquerda
$('#left').click( function() {
  turnLeft();
});
$('#right').click ( function() {
  turnRight();
});

$('#down').click( function() {
  fullBackward();
});

$('#stop').click ( function() {
  stopRobot();
});

Cada uma destas funções está definida no ficheiro script.js – que simplesmente atualizam o ficheiro update_state.php que contêm as instruções para ligação ao servidor (robô).

Estas funções passam os argumentos (a direção que desejamos), para o ficheiro robot_state.json usando a função $.get do jquey, consequentemente alterando o ficheiro robot_state.json

//Parar o robô
function stopRobot() {
 $.get( "update_state.php", {
 left: "0",
 right: "0",
 forward: "0",
 backward: "0"} );
 }
//Em frente
function fullForward(){
  $.get( "update_state.php", {
    left: "0",
    right: "0",
    forward: "1",
    backward: "0"}
   );
}

O ficheiro update_state.php não é mais que o ficheiro que se vai ligar ao servidor (o robô) e envia as informações dos movimentos.

Começa por abrir o ficheiro robot_state.json (que contém apenas uma string em formato JSON)

$string = file_get_contents("robot_state.json");
 $json_a= json_decode($string,true);

Ir buscar os valores do movimento

// Handle GET request
 $json_a['left'] = $_GET["left"];
 $json_a['right'] = $_GET["right"];
 $json_a['forward'] = $_GET["forward"];
 $json_a['backward'] = $_GET["backward"];

E atualizar o ficheiro JSON

// Save JSON file
 $fp = fopen('robot_state.json', 'w');
 fwrite($fp, json_encode($json_a));
 fclose($fp);

No sketch do arduino foi visto a parte do código que aguarda ligações de clientes – que o ficheiro update_state.php vai fazer. Em cima, está o código que vai buscar o movimento que desejamos, carregando o conteúdo do ficheiro JSON, atualizando-o conforme desejamos e ligar-se ao arduino.

// Create a TCP/IP socket & connect to the server
 $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
 $result = socket_connect($socket, "172.20.1.109", "8888");
// Request
 $in = "HEAD / HTTP/1.1rn";
 $in .= "Content-Type: text/htmlrn";
 $in .= $json_a['left'] . "," . 
 $json_a['right'] . "," .
 $json_a['forward'] . "," .
 $json_a['backward'] . ",rnrn";
 $out = '';

No final, recebemos a resposta do Arduino e fechamos a ligação

// Read answer
 while ($out = socket_read($socket, 4096)) {
 echo $out;
 }
// Close socket
 socket_close($socket);

Nota: No ficheiro update_state.php também têm que alterar o IP para o vosso arduino. A seguinte linha:

$result = socket_connect($socket, "172.20.1.109", "8888");

Alterem o IP “172.20.1.109” para o endereço do Arduino

Neste momento, temos o robot funcional e ao nosso comando !

Para que isto tudo funcione têm que ter um servidor Web a correr com suporte PHP e JSON.

 

Referências

Fantástico tutorial do Adafruit escrito por Marco Schwartz de onde grande parte do código foi retirado e usado.