Comecei a estudar o uso de servomotores com a intenção de usá-los para automatizar o controle de torneiras de 3 vias para a montagem de sistemas de análise em fluxo, como mostra a figura 320.
Figura 320. Servomotor controlando uma torneira de 3 vias (Fonte: Adding an actuated flow selector valve to our syringe pump)
Nas seções seguintes vamos descrever os módulos (físicos e lógicos) do sistema e como fazer a integração dos mesmos para a montagem de válvulas que permitam o controle de pequenas vazões em sistemas de análise em fluxo.
Para entender o funcionamento dessas válvulas vamos representar uma “Torneira de 3 Vias” por um diagrama esquemático como mostra a figura 321.
Agora vamos entender como podemos fazer a comutação dos fluxos entre 3 diferentes reservatórios fazendo rotações de 90° na torneira como mostra a figura 321.
Figura 322. Na posição “1” os reservatórios A, B e C estão conectados entre si. Após girar a torneira em 90° no sentido horário (Posição 2), os reservatórios A e B ficam interconectados e o reservatório C fica isolado. Após mais um giro de 90 ° no sentido horário (Posição 3), os reservatórios A e C ficam conectados e o reservatório B fica isolado. E finalmente após mais um giro de 90° no sentido horário (Posição 4), os reservatórios B e C ficam interconectados e o reservatório A fica isolado. Mais um giro de 90° leva de volta para a posição 1.
Mas é importante lembrar que o controle de uma torneira de 3 vias por um servomotor “convencional” está restrito ao limite de rotação de 180° dos servomotores.
Ou até menos (~120°), como foi o caso do servomotor que compramos.
Por exemplo, vamos imaginar uma aplicação na qual vamos usar um servomotor com giro máximo de ~120° para adicionar no reservatório A quantidades conhecidas dos reagentes contidos nos reservatórios B e C. Nesse caso seria necessário comutar o fluxo entre três reservatórios e executar apenas uma rotação de 90° e ~30° para bloquear as três saídas como mostra a figura 323.
Figura 323. Na posiçao 1 é possível transferir amostra ou reagente de B para A. Após um giro de 90° no sentido horário é possível transferir amostra ou reagente de C para A. E com mais um pequeno giro de ~30° no sentido horário todas as conexões ficam fechadas.
Os servomotores realizam rotações, com precisão, mas restritas a uma faixa de 0 a 180°. Eles possuem um pequeno motor conectado através de engrenagens a um eixo central que está ligado a um braço externo e a um potenciômetro. O potenciômetro gera um sinal para um circuito interno que controla a posição do eixo. (Figura 324)
Os servomotores possuem 3 fios: fase ou positivo (vermelho), terra ou negativo (preto ou marrom) e sinal de controle (amarelo, laranja ou branco). O fio vermelho deve ser ligado em 5V, o fio preto no terra e o fio de controle deve ser ligado a um dos pinos digitais com saída PWM da placa Arduino (pinos 3, 5, 6, 9, 10 e 11, com sinal "~").
Os servos são controlados com a aplicação de pulsos no fio de controle. Com um pulso curto de 1ms ou menos o servomotor gira para um extremo e um pulso longo de 2ms faz o servo girar para o outro extremo. Pulsos em valores intermediários fazem o servo girar com ângulos proporcionais à largura do pulso, como mostra a figura 325.
Mas os pulsos usados para o controle de servos são “diferentes” dos pulsos PWM gerados com o comando analogWrite.
E um servomotor pode ser “danificado” se for usado o pulso PWM gerado pelo comando analogWrite.
Por isso deve sempre ser usado o comando da biblioteca Servo.h
para o controle de servomotores com a placa Arduino.
As primeiras versões da biblioteca Servo.h
permitiam o controle de um servo apenas pelos pinos digitais 9 e 10. Mas as versões recentes permitem o uso de qualquer pino digital para controle de um servo.
Alguns links sobre a biblioteca Servo.h
:
Então providenciei a compra do servomotor Micro Servo 9g SG90 da Tower Pro (Datasheet) e fiz os primeiros testes de controle com o Arduino usando o seguinte código:
#include <Servo.h> #define SERVO 9 // Porta Digital 9 PWM Servo s; // Variável Servo int pos; // Posição Servo void setup () { s.attach(SERVO); Serial.begin(9600); s.write(0); // Inicia motor posição zero } void loop() { for(pos = 0; pos < 180; pos++) { s.write(pos); delay(15); } delay(1000); for(pos = 180; pos >= 0; pos--) { s.write(pos); delay(15); } }
Em seguida conectei uma torneira de 3 vias para verificar se esse servomotor era capaz de controlar a torneira e pude observar que o torque era suficiente para girar a torneira mas, aparentemente, exigindo muito “esforço” do “motorzinho”.
Então resolvi comprar um modelo com maior torque e escolhi um “genérico” do tipo Futaba S3003.
Esse servomotor “deve” ser alimentado por uma fonte externa de ~6V com capacidade de fornecer pelo menos 400mA (Fonte: Standard S3003 Servo - www.kitronik.co.uk/).
O único requisito é fazer o aterramento comum entre a placa Arduino e a fonte externa como mostra a figura 326.
Figura 326. Diagrama do circuito para alimentação de um servomotor com fonte externa (Fonte: Arduino: How to Use a Servo Motor With an External Power)
Segundo a mensagem: Dúvida Servo Futaba S3003?, esse modelo de servomotor opera com tensão de 4,8-6V e demanda corrente na faixa de 300 mA.
“Segundo os manuais, um servomotor é capaz de realizar rotações de até 180°. Mas nos primeiros testes pude verificar que o motor que eu havia adquirido girava, ‘no máximo’120°”.
Felizmente, para a nossa aplicação, isso não representa um grande problema. Mas dependendo do projeto isso pode inviabilizar o uso do motor.
Pedi ajuda no fórum https://forum.arduino.cc (Which version of servo.h is able to use any digital pin? e https://forum.arduino.cc (Servo don't rotate more than ~120 degrees) e fui informado que não é incomum encontrar servos que não giram 180°.
Também foi sugerido experimentar o comando writeMicroseconds() e experimentar diferentes valores além do intervalo padrão (1000 - 2000), mas tomando cuidado pois valores fora desses limites podem gerar altas correntes e danificar o servo.
Então fiz alguns testes com o comando writeMicroseconds() variando o intervalo de tempo de 1000 até 600 e de 2000 até 2400 conforme o código:
#include <Servo.h> #define SERVO 9 // Porta Digital 9 PWM Servo myservo; // Variável Servo int pos; // Posição Servo void setup () { myservo.attach(SERVO); Serial.begin(9600); myservo.write(1500); // Inicia motor posição zero } void loop() { for(pos = 600; pos < 2400; pos = pos + 10) { myservo.writeMicroseconds(pos); delay(15); } delay(2000); for(pos = 2400; pos >= 600; pos = pos - 10) { myservo.writeMicroseconds(pos); delay(15); } delay(4000); }
Mas o ângulo máximo de rotação continuou em ~120° e observei também que com o aumento da velocidade de rotação o motor apresentava variações na velocidade se movimentando “aos saltos”.
Para facilitar a automação do controle de uma torneira de 3 vias por um servomotor, seria conveniente implementar uma interface de controle que permitisse a conversão das posições da(s) válvula(s) em comandos para a placa Arduino que enviaria os comandos para o servomotor executar o deslocamento correto e fazer o alinhamento das conexões conforme desejado.
Isso poderia ser feito com um arquivo de configuração correlacionando as posições da torneira com diferentes ângulos de rotação.
Para o exemplo descrito na figura 323 poderíamos ter a seguinte tabela de conversão:
Tabela 28. Tabela de conversão das posições da torneira de 3 vias para o exemplo da figura 323
Posição | Ângulo |
---|---|
1 (conectado com o reservatório A) | 0° |
2 (conectado com o reservatório B) | 90° |
3 (fechado) | 120° |
Portanto o desafio proposto é: Como montar uma interface de controle "genérica" para permitir a automação do controle das válvulas de 3 vias para o seu uso em sistemas de análise em fluxo com diferentes configurações?
A interface deve ser amigável, permitindo ao usuário fazer uma programação dinâmica em uma linguagem simples e de alto nível.
O primeiro passo é implementar um programa no Arduino (sketch) para receber comandos pela porta serial no formato:
[comando];[pino];[valor]
Essa foi a estratégia usada no código do projeto Sistema com Bombeamento e Detecção - Irrigador com balança.
Mas é importante lembrar que nos pinos digitais podem estar conectados servomotores ou relês que recebem comandos diferenciados.
E portanto o [comando] deve chamar uma função específica, definida no “sketch” para o tipo de dispositivo conectado ao pino digital da placa Arduino.
A campo [pino] define o pino digital ao qual está conectado o dispositivo: D2, D3 (PWM), D4, D5(PWM), D6(PWM), D7, D8, D9(PWM), D10(PWM), D11(PWM) ou D12.
E o campo [valor] especifica o valor numérico que deve ser enviado.
Por exemplo, podemos ter o comando:
SET_SERVO - dedefinir a posição de um servomotor.
Poderíamos pensar também no comando GET_SERVO para retornar a posição do servo, mas a maioria dos servos não retornam informações sobre o ângulo atual do servo.
Uma alternativa seria usar o comando read() mas ele não retorna a posição real do servo mas apenas o valor usado na última chamada do comando write().
Mas mesmo assim o comando read() poderia ser útil para checar a comunicação com a placa Arduino.
Exemplos de comandos:
SET_SERVO;S1;0 - servomotor 1 (S1) deve girar até a posição de 0°.
SET_SERVO;S2;90 - servomotor 2 deve girar até a posição de 90°.
GET_SERVO;S1 - retorna o ângulo do servomotor 1.
Também é possível usar essa estratégia e definir funções específicas para outros tipos de sensores ou atuadores ligados na placa Arduino.
SET_VALVE;V4;1 - definir o valor 1 (ON) da válvula 4 .
SET_PUMP;P9;200 - definir o valor 200 (PWM) à bomba 9.
GET_VALVE;V4 - retorna o estado (0/1) da válvula 4.
GET_LEVEL_SENSOR;S2 - retorna a leitura do sensor “analógico” 2
Com essa abordagem, o número do pino (digital/analógico) ligado ao dispositivo (servomotor, válvula, bomba ou sensor) deverá ser definido no código (Sketch) do Arduino.
Seguindo essa estratégia, emplementei o seguinte código para o Arduino:
#include <Servo.h> int pos; // Posição Servo const unsigned int MAX_INPUT = 40; char message[MAX_INPUT]; char *cmd; char *pin; char *value; char c; byte length; byte length_message; Servo servo_1; // Variável Servo Servo servo_2; void setup () { //Available pins //D2, D3 (PWM), D4, D5(PWM), D6(PWM), D7, D8, D9(PWM), D10(PWM), D11(PWM) e D12 //A0, A1, A2, A3, A4 e A5 //Servo 1 ligado ao pino 9 servo_1.attach(9); servo_1.write(0); //Servo 2 ligado ao pino 10 servo_2.attach(10); servo_2.write(0); Serial.begin(9600); } // here to process incoming serial data after a terminator received void process_data (char *data) { cmd = strtok(data, ";"); if ( (strcmp(cmd, "SET_SERVO") == 0) || (strcmp(cmd, "set_servo") == 0) ) { pin = strtok(NULL, ";"); value = strtok(NULL, ";"); setServo(pin, value); } else if (strcmp(cmd, "GET_SERVO") == 0 || strcmp(cmd, "get_servo") == 0) { pin = strtok(NULL, ";"); getServo(pin); } else { Serial.print("unknown command: "); Serial.println(cmd); } // end of if } // end of process_data void setServo(char *pin, char *val) { if ((pin[0] == 'S') || (pin[0] == 's')) { byte numServo = strtol(pin+1, NULL, 10); byte state = atoi(val); switch (numServo) { case 1: servo_1.write(state); break; case 2: servo_2.write(state); break; } } } void getServo(char *pin) { byte numServo = strtol(pin+1, NULL, 10); switch (numServo) { case 1: Serial.println(servo_1.read()); break; case 2: Serial.println(servo_2.read()); break; } } void processIncomingByte (const byte inByte) { static char input_line[MAX_INPUT]; static unsigned int input_pos = 0; switch (inByte) { case '\n': // end of text input_line[input_pos] = 0; // terminating null byte // terminator reached! process input_line here ... process_data (input_line); // reset buffer for next time input_pos = 0; break; case '\r': // discard carriage return break; default: // keep adding if not full ... allow for terminating null byte if (input_pos < (MAX_INPUT - 1)) input_line[input_pos++] = inByte; break; } // end of switch }// end of processIncomingByte void loop() { // if serial data available, process it while (Serial.available () > 0) processIncomingByte (Serial.read()); }
Montei inicialmente um protótipo usando duas chapas de acrílico de 5mm de espessura e barras roscadas de ~8mm de diâmetro. Mas tive problemas de trincas na hora de fazer os furos na chapa de acrílico.
Aproveitei este protótipo para fazer os testes iniciais, mas providenciei a montagem de outra válvula usando chapas de acrílico mais finas (3mm) e barras roscadas de ~5mm conforme a figura 327
Figura 327. Diagrama esquemático com a visão superior das respectivas posições da válvula. Na posição 1 conectando os reservatórios A e C, e na posição 2 conectando os reservatórios B e C. E a visão lateral da estrutura de suporte para o servomotor e válvula de 3 vias respectivamente nas posições 1 e 2.
Tinha comprado servomotores da marca MG995 e MG996R. E após consultar o site do fabricante escolhi o modelo MG996R com a “promessa” de maior precisão.
Para a comunicação com a placa Arduino foi feita uma interface gráfica em Tcl/Tk:
#!/usr/bin/env wish #Interface to control a 3 way valve with a servomotor set fonte_grande {Times 18} #Info message label .msg -text "Interface para controle de válvula de 3 vias" -font $fonte_grande pack .msg -side top -pady 10 #Main frame frame .frame_control pack .frame_control -side bottom #Frames to each position labelframe .frame_control.frame_pos_1 labelframe .frame_control.frame_pos_2 labelframe .frame_control.frame_pos_3 pack .frame_control.frame_pos_1 .frame_control.frame_pos_2 .frame_control.frame_pos_3 \ -side left -ipady 5 #Create links to labels image create photo position_1 -format GIF -file Posicao_01.gif label .frame_control.frame_pos_1.image_position_1 -image position_1 image create photo position_2 -format GIF -file Posicao_02.gif label .frame_control.frame_pos_2.image_position_2 -image position_2 image create photo position_3 -format GIF -file Posicao_03.gif label .frame_control.frame_pos_3.image_position_3 -image position_3 pack .frame_control.frame_pos_1.image_position_1 \ .frame_control.frame_pos_2.image_position_2 \ .frame_control.frame_pos_3.image_position_3 \ -side top -pady 10 -padx 10 radiobutton .frame_control.frame_pos_1.pos_1 -text "Posição 1" -font $fonte_grande \ -variable position -value 1 \ -relief flat -indicatoron 0 \ -command { puts "Posição $position" puts $porta "SET_SERVO;S1;0" } radiobutton .frame_control.frame_pos_2.pos_2 -text "Posição 2" -font $fonte_grande \ -variable position -value 2 \ -relief flat -indicatoron 0 \ -command { puts "Posição $position" puts $porta "SET_SERVO;S1;86" } radiobutton .frame_control.frame_pos_3.pos_3 -text "Posição 3" -font $fonte_grande \ -variable position -value 3 \ -relief flat -indicatoron 0 \ -command { puts "Posição $position" puts $porta "SET_SERVO;S1;40" } pack .frame_control.frame_pos_1.pos_1 .frame_control.frame_pos_2.pos_2 .frame_control.frame_pos_3.pos_3 set porta [ open /dev/ttyACM0 r+ ] fconfigure $porta -mode 9600,n,8,1 -buffering none
É uma interface básica (Figura 328) apenas para testar o envio de comandos para a placa Arduino. Mas pode ser complementada com recursos para a programação de “scripts” para automatizar a operação de 1 ou mais válvulas.
Para reduzir o espaço ocupado pelo conjunto motor+válvula e ter uma estrutura mais fácil de montar e acomodar dentro de um sistema maior, providenciei a montagem de um novo protótipo.
A figura 329 mostra os 3 protótipos que foram montados.
Figura 329. Os 3 protótipos que foram montados para encontrar a melhor forma de conectar o servomotor com a válvula de 3 vias.
A figura 330 mostra as etapas de montagem e detalhes do terceiro protótipo.
O uso da parte superior de uma seringa de 5mL para fazer a conexão entre o servomotor e a válvula se mostrou mais simples de montar e mais compacta do que o tubo de PVC, como mostra a figura 331.
Figura 331. Etapas de montagem do conector entre a válvula e o servomotor usando a parte superior de uma sering de 5mL.
Em seguida o conector é parafusado na “hélice” do servomotor como mostra a figura 332.
Para manter o corpo da válvula e o servomotor ainhados foi montada uma estrutura com 2 placas de acrílico de 3 mm de espessura, e 4 barras roscadas de ~5mm de diâmetro conforme a figura 333.
Figura 333. Etapas de montagem das estruturas de acrílico para o alinhamento da válvula com o servomotor.
Para o controle adequado da válvula é necessário identificar os ângulos do servomotor correspondentes às posições desejadas. E para fazer essa calibração implementamos uma interface gráfica usando o widget “scale” como mostra a figura 334.
Também fizemos modificações no Sketch do Arduino para o envio de diversos comandos para a placa Arduino.
Essa abordagem está documentada na seção Arduino Multicomando.
Para fazer a calibração precisamos identificar as posições de 0° e 180° do eixo do servo e posicionar a hélice e a torneira nas posições desejadas respeitando os limites do eixo e o sentido de rotação do servomotor.
Em seguida fazemos deslocamentos sucessivos com o programa de calibração avaliando visualmente quais os ângulos que correspondem às conexões desejadas.
É difícil explicar esse procedimento com “palavras”. Você só vai entender “fazendo”. :-)