segunda-feira, 28 de setembro de 2015

Usando um Port Expander com Arduino

O MCP 23017 é um port expander, ele permite que você tenha mais entradas e saída que normalmente tem na Arduino. Por outro lado permite que você estenda a comunicação com apenas 4 fios (dois do I2C, um de alimentação e outro do terra).

As dicas aqui contidas resolveram problemas que eu encontrei, mas podem não ser a melhor forma de resolve-los, não exite em contribuir nos comentários.

Os exemplos de código consideram o uso da biblioteca da Adafruit para o MCP 23017.

É importante que alguns detalhes sejam observados:

1 - Coloque um capacitor de 1µF em paralelo com o botão (caso você use). Ative, no código, o pull-up interno do MCP23017 que é de 100KΩ, então teremos um resistor em série com o capacitor, quando o botão está aberto. E o tempo de suavização (anti-debounce) será de 0,1s (τ = RC => τ = 100K*1µ => τ = 0,1s).


O uso deste capacitor (um em paralelo para cada botão) além do efeito desejado que é a suavização durante o acionamento tem um efeito indesejado: quando o circuito é energizado o capacitor está descarregado e irá parecer para o MCP 23017 que ocorreu um acionamento do botão. Para eliminar este efeito indesejado é necessário incluir um delay de 60ms (testado empiricamente como um tempo adequado  20ms não deu certo 30ms sim, então escolhi 60ms para ter folga ) e uma leitura para limpar o buffer, no código da Arduino no setup depois da inicialização do MCP 23017.




#define button1 13
#define led 0

void setup() { 

//inicializa o MCP23017     
mcp.begin(); // use default address 0
mcp.setupInterrupts(true, false, LOW); // mirror habilitado - cada interrupcao em INTA e espelhada em INTB
//saídas (só para mostrar com se faz)    
mcp.pinMode(led, OUTPUT);//pino 0  mcp_PinA0 configurado com saída    
//entradas    
mcp.pinMode(button1, INPUT); // pino 13 mcp_PinB5 button1 configurado com entrada
mcp.pullUp(button1, HIGH); // ativa pullup interno de 100K
mcp.setupInterruptPin(button1, FALLING); // gera interrupcao quando de sai de nivel alto para nivel baixo

//tratamento para o uso dos capacitores em paralelo com os botões    
delay(60);//pequena espera antes da leitura inicial 
mcp.getLastInterruptPinValue();//leitura inicial, para limpar o buffer






2 - Cuidados no código com a interrupção

A) Quando se trabalha com Interrupções, uma boa prática é realizar o mínimo possível dentro da função  (interrupt service routine (ISR)) chamada pela interrupção, preferencialmente mude uma variável booleana e trate no loop. IMPORTANTE  está variável global e volatile, por exemplo:



volatile boolean interruptHappen = false;


Normalmente no loop você terá uma chamada que trata a interrupção, algo como:


if(interruptHappen)
    handleInterrupt();


mas ainda não implemente desta forma, veja o item C).


B) Para evitar que ocorra uma interrupção enquanto a função handleInterrupt() deve desligar a interrupção no seu inicio e ligar no final:

void handleInterrupt(){
  detachInterrupt(arduinoInterrupt);
   ....
  attachInterrupt(arduinoInterrupt,intCallBack,FALLING); }


C) É recomendável que se limite o intervalo mínimo entre uma interrupção e a próxima (depois de experimentar uma instabilidade, que travava o funcionamento do MCP 23017, eu resolvi com este intervalo mínimo, equivale a um anti debounce de software para a interrupção.


//variávei globais
volatile unsigned long button_time = 0;  //anti debounce de software na interrupção
volatile unsigned long last_button_time = 0; //anti debounce de software na interrupção
volatile int buttonInterruptInterval = 100; //intervalo mínimo entre uma interrupção e outra, evita problema de debounce
volatile uint8_t mcpPin;

...
dentro do loop:

if (interrupted)
     {
     button_time = millis(); //Anti debounce de software para a interrupção
     mcpPin=mcp.getLastInterruptPin(); //lê qual pino foi acionado
     if (((button_time - last_button_time) > buttonInterruptInterval)&&(mcpPin==button1))//somente trata se for um botão válido e em intervalos superiores a 100ms
       {
       handleInterrupt();
       last_button_time = button_time;
       }
     else mcp.getLastInterruptPinValue();//senão limpa o bus (ignora o acionamento)
     }

D) A função hanleInterrupt() deve ser a mais direta possível, não adicione código que poderia estar em outra função, veja um exemplo:



void handleInterrupt()
   {
   detachInterrupt(digitalPinToInterrupt(PIN_INTERRUPT));//desativa a interrupção para evitar multiplos acionamentos
     
   if(mcpPin==button1) {código de botão 1();} // false = desligado
     
   while( ! (mcp.digitalRead(button1) );//espera o botão ser solto
     
   updateLedsProfile();
   cleanInterrupts();
   attachInterrupt(digitalPinToInterrupt(PIN_INTERRUPT), intCallBack, FALLING);//reativa a interrupção
   }



E) Estará sendo usada comunicação I2C, que é uma comunicação serial, logo recomendo uma implementação como a usada acima, no passo C uma variável global volatil mcpPin é carregada usando mcp.getLastInterruptPin() esta mesma variável é usada dentro do handleInterrupt() para definir a ação, ao invés de carregar novamente. A ideia é: seja simples recebeu uma interrupção identifique o botão e trate.


Informações sobre debounce podem ser encontradas em:
http://www.gammon.com.au/forum/?id=10945

Informações sobre interrupções podem ser encontradas em: https://www.arduino.cc/en/Reference/AttachInterrupt

Atenção: Quando se trabalha com delay() no loop pode ocorrer instabilidade na chamada da função através de interrupção, mesmo com acionamento LOW (por exemplo). Esta instabilidade aumenta caso a interrupção seja acionada por transição como FALLING.

segunda-feira, 20 de julho de 2015

Breakout Board para módulo HC-05 Bluetooth

O módulo Bluetooth HC-05 é barato e fácil de usar, existem vários sites com instruções.

Para se ter acesso ao pinos necessários você pode soldar fios diretamente, ou usar uma breakout board. Muitos já são vendidos soldados em uma.

Neste post eu disponibilizo o desenho de uma PCB que serve de breakout board.

Como o HC-05 se comunica com 3,3v é necessário um divisor resistivo (R1-10K R2-20K) para conectar o RX (3,3v) do módulo ao TX (5v) de uma Arduino Uno, por exemplo, já vi projetos que fazem este divisor com 1K 2K, mas eu prefiro trabalhar com 10K 20K.


Caso a intenção seja conectar o módulo a uma Arduino Due (que trabalha com 3,3v) este divisor não é necessário, logo não é necessário montar R1 e R2, mas é necessário ligar o pad 1 e 2 do JP1 para ligar diretamente o RX ao pin header SV1.


A PCB é single layer, somente o Top, como a figura abaixo;


O resultado final com o módulo soldado ficou assim:



Para testar o funcionamento eu liguei a breakout board a uma Arduino Leonardo:

Arduino Leonardo           Breakout HC-05
3,3v -------------------------- 3,3v
GND --------------------------- GND
TX ---------------------------- RX  (ATENÇÃO é TX com RX)
RX ---------------------------- TX  (ATENÇÃO é RX com TX)

O código para testar está disponível em:
https://github.com/Marchanjo/SimpleBreakoutHC-05/tree/master/Firmware/BluetoothHC05


Finalmente, basta instalar uma app de Terminal Bluetooth no seu celular Android, eu usei esta:
https://play.google.com/store/apps/details?id=ptah.apps.bluetoothterminal&hl=en


Quando o módulo Bluetooth HC-05  é alimentado (3,3v e GND) ele já aparece na procura Bluetooth do seu celular (o nome pode ser HC-05 ou linvor) e normalmente para parear se usa 1234.

Montagem:



Com a Arduino Leonardo executando o código e conectada ao módulo, quando você abrir o terminal e conectar ao HC-05 (depois de já ter pareado, normalmente a senha de pareamento é 1234), o terminal irá apresentar o texto "teste"a cada segundo, este teste indica a transmissão da Arduino para o Celular.

Agora para testar o sentido ao contrário, é necessário abrir o monitor serial da IDE da Arduino
e no Terminal do celular digitar um carácter e enviar (botão Send), este mesmo carácter irá aparecer no monitor serial da Arduino, indicando a transmissão do Celular para a Arduino.


Todos arquivos necessários para fazer a PCB e o código da Arduino estão disponíveis aqui:
https://github.com/Marchanjo/SimpleBreakoutHC-05











quarta-feira, 11 de março de 2015

Atualização do bootloader da Pro Micro

 A placa de desenvolvimento Pro Micro (compatível Arduino Leonardo)  é pequena, tem conector micro USB, tem baixo consumo de energia, em suma: ideal para de eletrônica vestível e de internet das coisas. 




Ela pode vir de fábrica com um bootloader desatualizado, que tem como característica demorar 8 segundos para executar o seu sketch depois de energizar a placa e depois do reset.

Dependendo do tipo de projeto isto é um problema, felizmente a nova versão do bootloader da Arduino Leonardo (que pode ser usado na Pro Micro) tem um comportamento um pouco diferente, mas que resolve este problema, quando você energiza a placa ela carrega seu sketch quase que imediatamente, e somente espera 8 segundos depois do reset. Mais informações aqui.

Para atualizar o bootloader em uma Arduino UNO ou Leonardo é fácil pois já existe um conector ICSP:




Neste link mais detalhes: atualização do bootloader


Mas para atualizar o bootloader da Pro Micro é necessário preparar um cabo que acesse os seguintes pinos:



Pinout da Pro Micro:


Eu resolvi soldar um cabo para que não ocorresse mal contato, mas de maneira superficial para facilitar a posterior retirada. A outra ponta deste cabo é um conector fêmea, para encaixar na no conector ICSP da USBtinyISP.




Com uma placa como a USBtinyISP (que você pode montar com as instruções deste link) é possível carregar o novo bootloader.

A posição do conector ICSP na USBtinyISP é indicada na figura abaixo:





Já com o cabo conectado é indicado o Vcc e o sinal MISO (pino1)



Agora basta abrir a IDE da Arduino (eu fiz este procedimento com a versão 1.6.0 e 1.6.1). Quando fiz no Linux (com root) funcionou perfeitamente.

Executando a IDE:

[user@localhost arduino-1.6.1]$ su root

[root@localhost arduino-1.6.1]# ./arduino 

Em Ferramentas, selecione a placa Arduino Leonardo:



Em Ferramentas, selecione o programador USBtinyISP:

Em Ferramentas, selecione Gravar Bootloader:



 irá demorar um pouco... mais ou menos um minuto para atualizar.

Gravação com sucesso:



IMPORTANTE: Tentei várias vezes este processo no Windows e sempre dava erro ao gravar o bootloader:

avrdude: verifying ...
avrdude: verification error, first mismatch at byte 0x7080
         0x00 != 0x30
avrdude: verification error; content mismatch


avrdude done.  Thank you.

Ainda não descobri a causa deste erro.