sábado, 11 de dezembro de 2010

Demo: DS1307

O DS1307 é um circuito integrado com um relógio (RTC) que fornece uma solução simples para circuitos que precisem marcar a passagem de tempo ou mostrar as horas. De baixíssimo consumo, permite manter a contagem do tempo mesmo com o circuito principal desligado através de uma bateria de backup. A comunicação com o circuito é feita através de I2C.

O único componente externo necessário é um cristal de 32.768kHz para o oscilador. Caso não consiga encontrá-lo à venda, ele existe em praticamente qualquer circuito que tenha um relógio, desde relógios de pulso baratos até placas-mãe de computadores. Não é difícil encontrá-los em sucata. O cristal deve ser soldado o mais próximo possível do circuito integrado, com o invólucro metálico eletricamente ligado ao terra.

O DS1307 também tem um pino de saída que fornece um sinal de onda quadrada ajustável para várias frequências. Pode ser útil no caso de um relógio, por exemplo, quando desejamos piscar um led a cada segundo.



#include <Wire.h>
// endereço I2C do DS1307
#define DS1307_I2C_ADDRESS 0x68

// cria as variáveis para armazenar os valores
// SS[0-60], MM[0-60], HH[0-24], D[1-7], DD[1-31], MM[1-12], YY[00-99]
byte second, minute, hour, dayweek, day, month, year; 

char buffer[50]; // buffer para armazenar entrada serial
 
 
void setup() {
  Wire.begin(); // inicializa interface I2C
  Serial.begin(9600); // inicializa serial
  
}

void loop() {
  if (Serial.available()) {
    fillBuffer();
    setDateDS1307();
  }
   
  getDateDS1307(); // pega a hora atual e armazena
  writeSerial(); // envia a hora pela serial

  delay(500);
} 
  
// converte binário para BCD
byte dec2bcd(byte val){
  return ((val/0xA*0x10)+(val%0xA));
}

// converte BCD para binário
byte bcd2dec(byte val){
  return ((val/0x10*0xA)+(val%0x10));
}

// converte dois dígitos ASCII da entrada em um único byte
byte digitToByte(int x,int y){
  return (byte) ((buffer[x]&0x0F)*10+(buffer[y]&0x0F));
}

// converte caracteres ASCII da entrada em um byte com o dia da semana
byte dayToByte(int x,int y, int z){
  int temp = buffer[x]+buffer[y]+buffer[z];
  switch(temp){
    case 0x12A: return (byte) 1; break; // Mon
    case 0x12E: return (byte) 2; break; // Tue
    case 0x120: return (byte) 3; break; // Wed
    case 0x131: return (byte) 4; break; // Thu
    case 0x121: return (byte) 5; break; // Fri
    case 0x128: return (byte) 6; break; // Sat
    case 0x136: return (byte) 7; break; // Sun
    default: return (byte) 0;
    }
}

// converte caracteres ASCII da entrada em um byte com o mês
byte monthToByte(int x,int y, int z){
  int temp = buffer[x]+buffer[y]+buffer[z];
  switch(temp){
    case 0x119: return (byte) 1; break; // Jan
    case 0x10D: return (byte) 2; break; // Feb
    case 0x120: return (byte) 3; break; // Mar
    case 0x123: return (byte) 4; break; // Apr
    case 0x127: return (byte) 5; break; // May
    case 0x12D: return (byte) 6; break; // Jun
    case 0x12B: return (byte) 7; break; // Jul
    case 0x11D: return (byte) 8; break; // Aug
    case 0x128: return (byte) 9; break; // Sep
    case 0x126: return (byte) 10; break; // Oct
    case 0x133: return (byte) 11; break; // Nov
    case 0x10C: return (byte) 12; break; // Dec
    default: return (byte) 0;
    }
}

// lê dados da serial para o buffer de entrada
void fillBuffer(){
  delay(500); 
  int ptr =0;
  buffer[ptr++]=Serial.read();
  while(Serial.available() > 0) {
    buffer[ptr++]=Serial.read();
    if(buffer[ptr]=='/n'){
      break;
    }
  }
} 

// acerta a hora do DS1307 usando como entrada um pipe do comando date.
// Sat Dec 11 12:04:12 BRST 2010
void setDateDS1307(){
   second=digitToByte(17,18);
   minute=digitToByte(14,15);
   hour=digitToByte(11,12);
   dayweek=dayToByte(0,1,2);
   day=digitToByte(8,9);
   month=monthToByte(4,5,6);
   year=digitToByte(27,28);
   
   Wire.beginTransmission(DS1307_I2C_ADDRESS);
   Wire.send(0x00);
   Wire.send(dec2bcd(second));
   Wire.send(dec2bcd(minute));
   Wire.send(dec2bcd(hour));
   Wire.send(dec2bcd(dayweek));
   Wire.send(dec2bcd(day));
   Wire.send(dec2bcd(month));
   Wire.send(dec2bcd(year));
   Wire.endTransmission();
}

// pega a data e hora do DS1307
void getDateDS1307(){
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0x00);
  Wire.endTransmission();

  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);
  second=bcd2dec(Wire.receive() & 0x7f);
  minute=bcd2dec(Wire.receive());
  hour=bcd2dec(Wire.receive() & 0x3f);
  dayweek=bcd2dec(Wire.receive());
  day=bcd2dec(Wire.receive());
  month=bcd2dec(Wire.receive());
  year=bcd2dec(Wire.receive());
}


// envia a data e hora para a portal serial
void writeSerial(){
  Serial.print(hour, DEC);
  Serial.print(":");
  Serial.print(minute, DEC);
  Serial.print(":");
  Serial.print(second, DEC);
  Serial.print("  ");
  Serial.print(day, DEC);
  Serial.print("/");
  Serial.print(month, DEC);
  Serial.print("/");
  Serial.print(year, DEC);
  Serial.println();
}


Datasheet: http://pdfserv.maxim-ic.com/en/ds/DS1307.pdf

segunda-feira, 18 de outubro de 2010

Projeto: rover com suspensão rocker-bogie

Um dos meus projetos mais antigos mas ao mesmo tempo mais parados é o de um rover autônomo que conseguisse navegar sozinho por longas distâncias. A meta era conseguir ao menos uma volta no quarteirão, sem marcação de qualquer tipo, vencendo os obstáculos do caminho. Tempo não era questão, poderia demorar horas se preciso.

Tendo todos os componentes para a navegação à disposição, GPS, sensores e câmera, a grande questão que surgiu foi o design da parte mecânica. Em mais de um ano testando designs e partes diferentes, optei por usar uma suspensão rocker-bogie, semelhante ao design dos rovers da NASA.
 
A animação abaixo ilustra o funcionamento dessa suspensão.


Um dos desafios com robôs autônomos com rodas é enfrentar obstáculos. Por regra, um robô com rodas e sem suspensão não consegue ultrapassar obstáculos de altura maior do que o raio da roda. Como um obstáculo que esse robô com certeza terá de enfrentar são as guias das garagens e a calçada, caso tenha de descer, qualquer design tinha de levar isso em conta.

Deixando de lado a parte chata envolvida, a suspensão rocker-bogie facilita isso garantindo que todas as rodas mantém contato com o solo. Enquanto uma roda está subindo o obstáculo, as outras fornecem mais tração ajudando a "escalada". Quando a roda frontal e central chegam ao topo, elas ajudam a roda traseira a subir. O corpo é ligado à suspensão em apenas um ponto, preso por um diferencial, garantindo que mantém uma inclinação média em relação ao resto do rover.

Esse design é simples e eficiente, podendo ser construído sem molas ou amortecedores de qualquer tipo. Por outro lado, todas as rodas têm motores, e as rodas frontais e traseiras precisam de servos de controle para virar.



Depois de três protótipos com tubos de PVC para testar a idéia, esse é o estado atual do rover. As rodas de automodelismo com 10cm de diâmetro têm pneu de borracha, fornecendo ótima tração. Dentro de cada uma há espaço mais do que suficiente para embutir um servo de tamanho padrão convertido para rotação contínua. As rodas frontais e traseiras têm os servos de controle e conseguem virar cerca de 120°. O diferencial que liga ao corpo consegui de um carrinho de controle remoto de brinquedo e funciona perfeitamente para o fim.

O corpo foi construído de polietileno de alta-densidade e perfis de alumínio reforçando partes críticas. Decidi construí-lo mais alto do que o normal para facilitar passar por obstáculos pequenos ao invés de precisar escalá-los ou contorná-los.

Infelizmente cometi uma falha de design que provavelmente vai exigir correções complicadas, mas quero vê-lo andando assim e avaliar o quão bem funciona.

Apesar de não ter um vídeo do protótipo antigo em movimento, creio que a foto abaixo ilustra melhor a idéia. As caixas empilhadas alteram a altura sob cada roda, mostrando como todas mantém contato com o chão independentemente disso.


O Arduino estará no limite para este projeto. Dos pinos digitais, 0 e 1 serão para o GPS, 2-7 com os servos de movimentação, 8-11 com os servos de direção e os outros para i2C e sensores. Estou considerando a possibilidade de colocar outro Atmega 328 separado comunicando via i2c apenas para sensores de contato e acelerômetro.

Projeto: robô hexápode

Meu último projeto com Arduino Mega é este robô hexápode. Com 3 graus de movimento em cada perna, ele é capaz de realizar movimentos bem complexos.



Apesar do microcontrolador de 8 bits limitar bastante o movimento por não ser muito adequado aos cálculos de cinemática reversa, apenas com tabelas de lookup é possível implementar vários gaits e realizar movimentos interessantes.




O corpo foi construído em polietileno de alta densidade. Os servos são 12 TowerPro MG995, com engrenagens de metal e alto torque, e 6 TowerPro SG5010. Não são a melhor alternativa, mas em um país onde paga-se 100% de imposto em produtos importados sem equivalente nacional, é o melhor que pude encontrar dentro do orçamento que tinha.

A comunicação é feita por um módulo XBee 2.5 de 2mW, ligado ao Arduino Mega com um shield do sparkfun.com. Na foto ele está com uma bateria de NiMh de 1800mAh, mas o robô já está com baterias de íons de lítio com 2400mAh.

O vídeo abaixo mostra alguns dos gaits já programados, incluindo alguns tropeços.




A idéia é em breve substituir o Arduino Mega por um microcontrolador STM32F103 de 32 bits, capaz de realizar os cálculos trigonométricos necessários para cinemática reversa em tempo real, além de melhor monitorar o contato das patas com o solo.

terça-feira, 12 de outubro de 2010

Demo: HMC6352

O HMC6352 é uma bússola digital que fornece uma solução simples e de baixo custo para aplicações como robôs e sensores remotos. Com a comunicação feita por I2C, a utilização é bem simples, com os dados de orientação já prontos para uso e resolução de 0.5 grau.

Para este demo usei a placa do Sparkfun.com ligada diretamente ao Arduino Duemilanove com os pinos de SCL e SDA conectados ao analog 5 e 4 respectivamente, sem resistores de pull-up.

O endereço i2c do HMC6352 é 0x42, mas como a lib Wire do Arduino  usa endereços de 7 bits ele é ajustado para 0x21.

Apesar de ter várias instruções para calibrar, salvar e recuperar valores da memória, a única instrução usada para obter os dados é o byte 'A'.


#include <wire.h> 

// endereço do HMC6352 já ajustado para 7 bits
#define ADDRESS 0x42 >> 1

int heading;


void setup()
{
Serial.begin(9600);

// inicializa o i2c
Wire.begin();
}


void loop()
{
  // i2c master inicia a transmissão
  Wire.beginTransmission(ADDRESS);
  // envia o comando 'A', "get data"
  Wire.send("A");
  // encerra a transmissão
  Wire.endTransmission();
  // para responder o comando "get data" ele
  // pode demorar até 6 ms
  delay(6);

  // solicita 2 bytes
  Wire.requestFrom(ADDRESS, 2);
  if (2 <= Wire.available()){
    // ajusta o byte mais significante e soma
    heading = Wire.receive();
    heading = heading << 8;
    heading += Wire.receive();
  }   
  
  // exibe o resultado final em graus
  Serial.println(heading/10.0);

  
  delay(500);
} 


Datasheet: http://www.sparkfun.com/datasheets/Components/HMC6352.pdf

Tutorial: Parte 2

Componentes necessários: 

Nenhum :)

Nível de dificuldade:

Básico


Apesar de haver vários modelos diferentes, este tutorial assume que o leitor usa um Arduino Duemilanove ou algum "genérico" semelhante. No futuro falarei de aplicações específicas de outros modelos e até como construir um.



Para começar a  fazer algo você precisará do cabo USB para conectar o Arduino ao computador, caso ainda não tenha. É um cabo de USB tipo A para tipo B, com conectores como os da foto abaixo. Se você tem uma impressora USB, provavelmente tem um cabo desses.



Antes de conectar o Arduino é preciso instalar o ambiente de desenvolvimento. Faça o download da versão mais recente para o seu sistema operacional: http://arduino.cc/en/Main/Software

Após a instalação, conecte o cabo ao Arduino e plugue em uma porta USB. O led verde deve acender. 

No Windows, assim que o sistema operacional detectá-lo, você deve passar pela novela de sempre de instalação de drivers, mas com sorte tudo deve  instalar automaticamente. 

Com qualquer distribuição Linux recente você já terá o driver adequado no kernel, mas precisará instalar os seguintes pacotes ou seus equivalentes na sua distribuição:
  • sun-java6-jre
  • avr-gcc-c++
  • avr-gcc
  • avr-libc

Com tudo  no lugar, podemos então começar a brincar um pouco. Primeiro vamos à anatomia da placa:


Os pinos no topo numerados de 0 a 13 são entradas e saídas digitais. Estes pinos são usados para interagir com qualquer dispositivo de entrada ou saída que lida com apenas dois estados. Na parte inferior direita estão os pinos de entrada analógica. Estes são usados para ler um valor que varia e não apenas dois estados, como um controle de volume ou velocidade. Veremos mais sobre isso adiante. À esquerda destes estão os pinos de alimentação.

Se tudo estiver bem, ao conectar seu Arduino à porta USB o led verde (PWR) deve acender. Após alguns instantes o led amarelo (L) logo abaixo do pino 13 deve começar a piscar. Inicie o ambiente de desenvolvimento (IDE).




Selecione a sua placa no menu Tools>Board, "Arduino Duemilanove or Nano w/ ATmega 328". Em seguida selecione a porta correta em Tools>Serial Port. No Windows as portas aparecerão como COM X, onde X é um número arbitrário. No Linux deve aparecer como /dev/ttyUSB0, provavelmente já selecionado. Com isso você está pronto para enviar seus programas para o Arduino.

No menu File>Examples>Digital selecione Blink. Isso abrirá outra janela do IDE com o código de exemplo abaixo:


int ledPin =  13;    // LED conectado ao pino 13 
 
// A função setup() é executada uma vez quando o programa inicia
void setup()   {                
  // inicializa o pino como uma saída digital
pinMode(ledPin, OUTPUT);     
}

// a função loop() é executada continuamente, enquanto o Arduino 
// estiver ligado
void loop()                     
{
  digitalWrite(ledPin, HIGH);   // acende o LED
delay(1000);                  // espera 1 segundo
digitalWrite(ledPin, LOW);    // apaga o LED
delay(1000);                  // espera 1 segundo
} 


Essa é a estrutura básica de qualquer programa do Arduino. A função setup() deve conter o código que será executado uma vez, quando o Arduino iniciar. A função loop() será executada enquando o Arduino estiver ligado e deve conter a funcionalidade principal do programa.

O programa acima é o "Hello World" do Arduino, mas já utiliza três funções bem comuns.

A função pinMode(pino, modo) define o modo de funcionamento de um pino digital, se será usado como entrada ou saída.

A função digitalWrite(pino, valor) altera o nível de saída do pino de acordo com o desejado. Note que para isso é preciso que o pino tenha sido configurado como saída anteriormente usando a função pinMode.

A função delay(ms) faz com que o Arduino espere um determinado período de tempo definido em milissegundos antes de continuar a execução do programa.


Vá no menu Sketch>Compile, ou use CTRL + R, ou ainda o botão de "play" na barra de ferramentas no topo do IDE para compilar o programa. Se a compilação falhar e você não tiver feito nenhuma alteração, provavelmente há algo de errado com a sua instalação e haverá uma mensagem de erro no buffer preto na parte inferior do IDE.

Se a compilação foi bem sucedida, basta enviar para o arduino. Vá em File>Upload to I/O board, ou use CTRL + U, ou o botão com a seta apontando para a direita na barra de ferramentas. Se tudo estiver certo, em alguns instantes os dois leds amarelos (TX e RX) do Arduino devem começar a piscar até que o upload de todo o programa seja feito. Se o upload falhar, você pode ter selecionado a placa ou a porta errada anteriormente, ou há algum outro problema, e haverá uma mensagem de erro.

Se tudo correu bem, o LED amarelo (L) deve começar a piscar a cada segundo.

Para este teste usamos o pino já existente na placa, mas a mesma coisa pode ser feita com qualquer outro ligando um LED e resistor externos.

Na próxima parte mostrarei como usar outros pinos da mesma forma, além de como usá-los para entrada digital.

Review: Sensor Sharp GP2D120

O sensor de distância por infravermelho GP2D120 da Sharp é o mais preciso e com melhor sensibilidade da linha, porém menor alcance, de 4 a 30cm. Pode ser bem útil em aplicações como detectores e robôs, mas ele tem várias pegadinhas que só descobri depois de um bom tempo de pesquisa e experimentação.
A primeira delas é a pinagem. Como pode-se ver na imagem do datasheet do componente, os pinos não seguem o padrão comum em outros componentes, negativo-positivo-sinal, mas sinal-negativo-positivo. Cuidado para não inverter.


A segunda pegadinha é o conector. A distância entre os pinos é de 2mm, e não de 2.54mm como estamos habituados, além da capa um pouco diferente do JST . Ao comprar confira se a loja também vende o fio com o conector e compre ou terá dor de cabeça, pois ele é muito difícil de encontrar. Como alternativa você pode remover o conector e soldar fios.

Terceira, o sensor é muito suscetível a interferências, então é praticamente imprescindível o uso de um capacitor de ao menos 10uF entre o positivo e negativo da alimentação o mais próximo possível do sensor. Experimentando eu percebi que o ideal mesmo é ele ficar no sensor, e no meu caso foi fácil quando removi o conector para soldar fios como mencionei acima, mas se estiver fazendo um serviço limpo e tiver o cabo, provavelmente terá de experimentar um pouco.


A quarta pegadinha é que com um tempo médio de resposta de 39ms, não adianta tentar ler o valor do sensor com mais frequência do que isso. Um delay de 50ms entre as leituras costuma dar um bom resultado.

Por fim, o mais complicado. Apesar de algumas lojas propagandearem que a saída analógica faz com que seja fácil de usar, isso só seria verdade se a saída fosse linear. Como pode ver no gráfico do fabricante abaixo, a saída do sensor é uma curva bem doida.


Essa curva tem várias implicações. O pico no início é a distância mínima, e dá pra perceber por isso que quando a distância ao obstáculo diante do sensor é menor do que o mínimo, ele vai reportar o mesmo valor que daria para uma distância maior. Isso pode significar um robô batendo na parede achando que não tem nada, por exemplo. Outra coisa importante é que a curva não é muito uniforme e a medida dentro de determinada faixa é mais precisa do que outras. Na prática a resolução acima de 10cm é praticamente a metade daquela de 4 a 10cm.

A maior implicação dessa não-linearidade é a complicação que introduz quando é necessário realizar uma medida de fato. Se a sua aplicação exige apenas fazer algo acima ou abaixo de determinada distância, não há problemas. Porém se você deseja de fato computar a distância, precisará corrigir os dados.

Uma maneira de fazê-lo é usando uma tabela, porém ela pode ocupar muito espaço na memória. Outra maneira é usar uma aproximação. como quebrar a curva em várias linhas. Um artigo no Acroname Robotics demonstra uma maneira simples de gerar uma aproximação com uma única equação simples obtida depois de gerar uma constante a partir dos dados do sensor para diversas distâncias. Para o GP2D120 a equação é apenas:

D = (2914 / (v + 5)) - 1

O código de exemplo abaixo demonstra o uso.



void setup() {
   // inicializa a porta serial
   Serial.begin(9600);
 }
 
 void loop() {
   // le o valor do sensor
   int v = analogRead(0);
   // faz uma aproximacao
   float dist = (2914.0 / (v + 5)) - 1;
   
   // exibe o resultado
   Serial.println(dist, DEC);

   // aguarda 50ms
   delay(50);
}


Tutorial: Parte 1

Afinal, o que é esse tal de Arduino? Se você ainda não sabe ou se é um completo iniciante em eletrônica, então esse post é por onde você deve começar.

Vamos à definição oficial:

"Arduino é uma ferramenta para construir computadores que podem sentir e controlar uma maior parte do mundo físico do que o seu computador pessoal comum. É uma plataforma de computação open-source baseada em uma placa com um microcontrolador simples, e um ambiente de desenvolvimento para escrever o software." [1]


Ótimo. É uma descrição bem clara para quem já sabe do assunto, mas o que é que ela quer dizer ?

Se você está lendo isso então com certeza você tem acesso a um computador. Seja um desktop ou um notebook seu computador tem periféricos para controlá-lo, como mouse, teclado, e outros para ele mostrar o resultado, como monitor, impressora, alto-falantes, etc.

Se quiser conectar algo inteiramente novo ao seu computador você precisa de conhecimento em várias áreas. Você precisa conhecer eletrônica caso não queira destruir o computador ao fazer a ligação, precisa conhecer uma linguagem de programação para que seu equipamento possa fazer alguma coisa, e precisa conhecer o objeto da interação e como comunicar-se com ele. O Arduino é uma ferramenta que integra estas três coisas de uma forma fácil de usar.

A parte eletrônica é baseada em uma placa com um microcontrolador e alguns componentes adicionais. Um microcontrolador é nada mais que um computador bem simples, mas reunindo o processador, memória e dispositivos de entrada e saída de dados em um único chip. A placa possui entrada USB para fácil conexão ao computador, pinos de ligação e uma entrada para fonte de alimentação.
Porém, programar diretamente um microcontrolador pode ser uma tarefa árdua para um iniciante. Há diferentes arquiteturas, famílias e modelos, e pequenas idiossincracias de cada um, além da complexidade natural do processo. Aí entra a outra parte do Arduino, o software.

Uma das partes do software é o bootloader. Simplificando, é um software que vem gravado no microcontrolador do Arduino e que permite gravar os programas que você vai escrever no computador diretamente, através da porta USB. Sem ele seria preciso utilizar um programador ou um cabo especial.

A outra parte é o ambiente de desenvolvimento. Ele é um software semelhante a um editor de textos que oferece algumas facilidades para escrever seus programas, e faz a comunicação com o bootloader, permitindo que você envie seus programas para o Arduino.



Combinando esses elementos você pode combinar formas de entrada e saída de dados, construindo novos equipamentos ou novas formas de interagir com seu computador. Dispositivos de entrada podem ser botões, chaves, sensores de luz, som, temperatura, gás, etc. Dispositivos de saída podem ser luzes, auto-falantes, motores, etc.

No meio disso pode haver comunicação direta com o computador ou não. Com os componentes e o código certo, qualquer tipo de interação é possível.

Finalmente, o Arduino é uma plataforma open-source. Isso significa que tanto o software quando o hardware são disponíveis livremente. O código fonte do software é disponível para modificar e estudar, assim como os esquemas do hardware. Você pode incluir nos seus projetos e pode até mesmo modificá-los como quiser.


[1] http://www.arduino.cc/en/Guide/Introduction

Introdução

Bem vindo ao blog Arduinianos. A idéia é reunir aqui uma série de posts com artigos direta ou indiretamente relacionados ao Arduino. Apesar de eu escrever sobre meus projetos no meu blog pessoal, aqui é um espaço dedicado a textos mais didáticos, de diversos níveis de complexidade.

Um dos objetivos é manter um tutorial bem linear, partindo do básico, falando também de eletrônica para aqueles que realmente nunca pegaram em um ferro de solda poderem aprender e se interessar. Além do tutorial, farei periodicamente posts variados de nível mais intermediário e avançado, demonstrações e reviews de componentes.

Posts com título "Tutorial" farão parte do tutorial, mantendo a linearidade, do básico ao avançado. Aqueles com "Review" são os que detalharão componentes e features que já usei bastante. Já "Demo" são aqueles que terão apenas um exemplo de código e alguma explicação básica da funcionalidade. "Projeto" são aqueles em que vou falar dos meus projetos empregando o Arduino.

Código fonte de exemplos e outros arquivos de minha autoria estarão disponíveis no repositório github em http://github.com/pjwerneck/Arduinianos.