Olá, no tutorial de hoje, vamos aprender a programar a lógica de um braço robótico, demonstrando cada etapa do desenvolvimento do código e seu sistema de gravação e reprodução de movimentos. Vamos lá !!
Neste tutorial completo, você aprenderá como programar um braço robótico utilizando a placa Arduino Pro Micro. O tutorial aborda desde a montagem do hardware até a criação e configuração do código, permitindo controlar os movimentos do braço robótico com precisão. Ideal para makers, entusiastas de robótica e estudantes de tecnologia, este projeto utiliza servo motores controlados por PWM, o que facilita o movimento articulado de cada junta do braço. O passo a passo inclui explicações detalhadas sobre os componentes, o funcionamento do código e como conectar tudo de forma simples e eficaz. Quer você seja iniciante ou tenha mais experiência com Arduino, este guia ajudará a criar um projeto funcional e interativo de automação robótica.
Este tutorial será divido nos seguintes tópicos, e caso deseje ir para algum dos tópicos:
Variáveis de controle dos servos e seus valores de leitura
Definição de Variaveis de controle
Reprodução dos movimentos gravados
Video do braço robótico funcionando
Considerações
Para começar a programar o braço robótico, primeiro é necessário definir o eixo 0 do motor corretamente.
Lista de materiais
Para iniciarmos a programação, vamos precisar dos seguintes componentes:
01 - Estrutura do braço robótico
01 - Arduino Pro micro
01 - Fonte p/ Protoboard - 12V p/ 5,0/3,3 V - Branco
01 - Kit Jumper Macho Fêmea - 20 pçs
02 - Módulo Joystick Analógico - 3 Eixos
01 - Kit Jumpers Rígidos Formato de U - 140 pçs
Inclusão da biblioteca Servo
No Início do código, a biblioteca Servo.h é incluida.
#include <Servo.h>
A Servo.h facilita o controle de servomotores em microcontroladores como o Arduino. Ela permite associar pinos digitais a objetos Servo e envia comandos para definir a posição do motor, além de controlar o movimento angular dos servos com valores entre 0 e 180 graus.
Definição dos objetos e Pinos
Com a bilioteca incluida, devemos definir quatro objetos, sendo cada um para o controle de um motor do braço robótico:
Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;
Nesta parte do código, ocorre a definição de quatro objetos de classe Servo, e cada um desses objetos controlará um motor. Sendo assim, cada objeto fica responsável da seguinte maneira:
Servo1: Controla a base do braço.
Servo2: Controla a extensão do braço.
Servo3: Controla a altura do braço.
Servo4: Controla a garra do braço.
Com os objetos definidos para controle do servo motor, definiremos agora, os pinos do joystick, que são os pinos utilizados para controlar a posição dos motores, como veremos mais a baixo:
Pinos dos potenciômetros ( O módulo Joystick, possui em sua construção, dois potênciometros)
int potpin1 = A0;
int potpin2 = A1;
int potpin3 = A3;
int potpin4 = A2;
Os pinos do joystick são definidos como potpin1 a potpin4 e são pinos analógicos. E eles estão conectados aos potenciômetros do módulo Joystick que serão usados para controlar manualmente os servos. Nesse parte do código, os valores dos potênciometros ficam da seguinte maneira:
potpin1 = A0; - Controla a Base
potpin2 = A1; - Controla a extensão
potpin3 = A3; - Controla a altura
potpin4 = A2; - Controla a Garra
Variáveis de controle dos servos e seus valores de leitura
Definição das váriaveis
int val1, val2, val3, val4;
int s1 = 90, s2 = 50, s3 = 10, s4 = 25;
Define as variaveis val1 a val4 para armazenar os valores analógicos entre 0 e 1023 lidos pelos pinos dos potênciometros.
Essa variaveis são definidas para armazenas o valor dos ângulos atuais dos servos. As variaveis s1 a s4 estão definidas para a posição inicial, em que o braço robótico fique parado na posição definida como 0, no começo do código.
Variaveis e Arrays para Gravação dos movimentos
const int maxSteps = 500; // Reduzido para economizar memória
int recordedSteps = 0;
byte pos1[maxSteps], pos2[maxSteps], pos3[maxSteps], pos4[maxSteps];
maxSteps: O número máximo de passos (ou posições) que o sistema pode gravar durante o processo de gravação de movimentos. Esse limite é definido para evitar problemas de memória do Arduino.
recordedSteps: Mantém o número de passos gravados.
byte : Essa variável definida como byte utiliza 8 bits de memória que armazenam valores entre 0 e 255, que é suficiente para os ângulos dos servo motores que vão de 0 a 180º graus.
pos1[maxSteps], pos2[maxSteps], pos3[maxSteps], pos4[maxSteps]: Definem um Array para cada servo motor durante a gravação, e depois utiliza esse array para reproduzir os movimentos.
Definição de Variaveis de controle
Aqui são definidos as variaveis necessárias para verificar o estado atual do modo, que mais a frente, verifica em qual modo o microcontrolador se encontra, se é o de gravação ou reprodução.
bool isRecording = false;
bool isReplaying = false;
int replayIndex = 0;
isRecording: Variável booleana que indica se o sistema está no modo de gravação de movimentos.
isReplaying: Variável booleana que indica se o sistema está no modo de reprodução de movimentos gravados.
replayIndex: Controla o índice atual no processo de reprodução de movimentos.
Controle de tempo com millis
Aqui são definidas as variáveis de controle de tempo, necessárias para contagem e controle dos Leds.
unsigned long previousMillis = 0;
const long interval = 500; // Intervalo de 500ms para piscar o LED
previousMillis: Usado para armazenar o tempo anterior (em milissegundos) para comparação com o tempo atual. É utilizado para controlar o intervalo entre as piscadas dos LEDs.
interval: O intervalo de tempo (500ms) usado para alternar o estado dos LEDs durante a gravação de movimentos.
Função Setup
Na função Setup, temos algumas definições de pinos e também a escrita da posição inicial do braço do motor.
void setup() {
pinMode(14, INPUT_PULLUP); // Botão de gravação
pinMode(15, INPUT_PULLUP); // Botão de reprodução
pinMode(17, OUTPUT); // LED indicador de gravação
pinMode(30, OUTPUT); // LED indicador de reprodução
Serial.begin(9600);
servo1.attach(10); // base, pino digital 10
servo2.attach(5); // extensão, pino digital 5
servo3.attach(6); // altura, pino digital 6
servo4.attach(3); // garra, pino digital 3
servo1.write(s1);
servo2.write(s2);
servo3.write(s3);
servo4.write(s4);
digitalWrite(17, 1); // Apaga LED de gravação inicialmente
digitalWrite(30, 1); // Apaga LED de reprodução inicialmente
}
Nas funções pinMode os pinos são configurados como entradas com resistores de pull-up internos para os pinos 14 e 15, sendo utilizados para gravação e reprodução. Já os pinos 17 e 30, são definidos como saída e são eles que controlam os leds presentes na placa Arduino Nano.
A função Serial.begin é chamada para funções de debug do código, para conseguirmos definir o zero, inicial do motor.
Servo.attach() é uma função que associa os pinos digitais aos servos correspondentes. E as funções servo1.write(s1) até servo4.write(s4) escrevem os valoes definidos mais acima como a posição inicial do braço robótico, que é baseada nas variáveis s1 a s4.
Definindo a escrita digital dos pinos 17 e 30 como 1 (HIGH) , os LEDs são definidos como apagados.
Função Loop
A função loop é onde acontece a lógica de controle em tempo real, leitura dos potenciômetros, gravação, reprodução e controle dos LEDs.
if (!isReplaying) { // Permite gravação e controle manual quando não está reproduzindo
val1 = analogRead(potpin1);
if (val1 < 100) {
s1 = s1 - 2;
if (s1 <= 10) {
s1 = 10;
}
servo1.write(s1);
delay(25); // Adiciona um delay para movimento mais lento
}
if (val1 > 900) {
s1 = s1 + 2;
if (s1 >= 170) {
s1 = 170;
}
servo1.write(s1);
delay(25); // Adiciona um delay para movimento mais lento
}
// Controle da extensão do braço (s2)
val2 = analogRead(potpin2);
if (val2 < 100) {
s2 = s2 + 2;
if (s2 >= 140) {
s2 = 140;
}
if (s2 >= 120 && s3 <= 20) {
s2 = 120;
}
servo2.write(s2);
delay(25); // Adiciona um delay para movimento mais lento
}
if (val2 > 900) {
s2 = s2 - 2;
if (s2 <= 10) {
s2 = 10;
}
if (s2 >= 120 && s3 <= 20) {
s2 = 120;
}
servo2.write(s2);
delay(25); // Adiciona um delay para movimento mais lento
}
// Controle da altura do braço (s3)
val3 = analogRead(potpin3);
if (val3 > 900) {
s3 = s3 - 2;
if (s3 <= 10) {
s3 = 10;
}
if (s2 >= 120 && s2 < 130 && s3 <= 20) {
s3 = 20;
}
else if (s2 >= 130 && s2 < 140 && s3 <= 30) {
s3 = 30;
}
else if (s2 == 140 && s3 <= 65) {
s3 = 65;
}
servo3.write(s3);
delay(25); // Adiciona um delay para movimento mais lento
}
if (val3 < 100) {
s3 = s3 + 2;
if (s3 >= 170) {
s3 = 170;
}
if (s2 >= 120 && s2 < 130 && s3 <= 20) {
s3 = 20;
}
else if (s2 >= 130 && s2 < 140 && s3 <= 30) {
s3 = 30;
}
else if (s2 == 140 && s3 <= 65) {
s3 = 65;
}
servo3.write(s3);
delay(25); // Adiciona um delay para movimento mais lento
}
// Controle da garra do braço val4 = analogRead(potpin4);
if (val4 < 100) {
s4 = s4 + 2;
if (s4 < 0) {
s4 = 0;
}
servo4.write(s4);
delay(25); // Adiciona um delay para movimento mais lento
}
if (val4 > 900) {
s4 = s4 - 2;
if (s4 > 30) {
s4 = 30;
}
if(s4 < 0)
{
s4 = 0;
}
servo4.write(s4);
delay(25); // Adiciona um delay para movimento mais lento
}
Entrando um pouco na lógica que acontece na função Loop, temos:
if (!isReplaying) { // Permite gravação e controle manual quando não está reproduzindo
val1 = analogRead(potpin1);
if (val1 < 100) {
s1 = s1 - 2;
if (s1 <= 10) {
s1 = 10;
}
servo1.write(s1);
delay(25); // Adiciona um delay para movimento mais lento
}
if (val1 > 900) {
s1 = s1 + 2;
if (s1 >= 170) {
s1 = 170;
}
servo1.write(s1);
delay(25); // Adiciona um delay para movimento mais lento
}
em if (!isReplaying) temos uma variável que permite realizar o controle e a gravação, quando o código não está reproduzindo o que foi gravado.
Já na linha que temos val1 = analogRead(potpin1), Definimos que a variável de nome Val1, é igual a leitura analógica do potênciometro do pino 1. Que é o pino do Joystick, utilizado para controlar a base, como foi definido mais acima.
Após a definição do valor da váriavel val1, o if seguinte:
if (val1 < 100) define que: se a variavel val1 for menor que 100
{
s1 = s1 - 2; - o valor da variável s1 vai ser igual ao valor atual de s1 menos 2, ou seja, se s1 for por exemplo 50, o valor de s1, será 50 - 2, ou seja 48.
if (s1 <= 10) - Se o valor s1 for menor do que 10
{ s1 = 10; } - Define o valor de s1 como 10. Isso define o limite máximo da variável s1, que será escrita no servo1, mais abaixo.
servo1.write(s1); - Aqui, o valor do ângulo, armazenado na variável s1 é escrito no servo1.
delay(25); - Aqui, um delay é definido para diminuir a velocidade de leitura.
}
Com isso, essa lógica, segue a mesma para os outros motores, e por exemplo, no caso, do servo do braço, mais algumas comparações são feitas e limites são definidos para que o braço não ultrapasse o "chão" e acabe erguindo a estrutura inteira.
Gravação dos movimentos
Durante a gravação de movimentos, o estado de isRecording é verificado, junto a recordedSteps, que tem que ser menor que o valor de maxSteps. Nessa parte do código, os arrays definidos antes do setup são utilizados para gravar os valores de s1 a s4. Com um pequeno delay para evitar multiplas leituras de uma só vez.
if (isRecording && recordedSteps < maxSteps) {
pos1[recordedSteps] = s1;
pos2[recordedSteps] = s2;
pos3[recordedSteps] = s3;
pos4[recordedSteps] = s4;
recordedSteps++;
delay(25);
}
isRecording: Quando está gravando, as posições atuais dos servos são armazenadas nos arrays pos1 a pos4, permitindo registrar os movimentos para reprodução futura.
Iniciar e parar gravação
Nessa função quando o botão 14 é pressionado e não está reproduzindo na !isReplaying, o estado de gravação da variável !isRecording é alternado. Se a gravação parar, os LEDs dos pinos 17 e 30 serão apagados e gravação finalizada é enviada na Serial. Se a gravação começar, o recordedSteps é reiniciado. Também é adicionado um delay para evitar um debounce do botão.
// Iniciar/parar gravação
if (digitalRead(14) == LOW && !isReplaying) {
isRecording = !isRecording;
if (!isRecording) {
Serial.println("Gravação finalizada");
digitalWrite(17, 1); // Garante que ambos os LEDs estejam desligados
digitalWrite(30, 1);
} else {
recordedSteps = 0; // Reseta a gravação para começar do início
Serial.println("Gravação iniciada");
}
delay(500); // Debounce do botão
}
Controle dos LEDs durante a gravação
Nessa parte do código, quando a gravação é iniciada, há uma definição de currentMillis = millis(); onde o valor de currentMillis é definido, para que haja a comparação mais a baixo, onde curretMillis - previousMillis tem que ser maior ou igual a interval que no começo do código, foi definido como 500 ms. Se essa condição for verdadeira, o valor de previousMilis = currentMillis. Isso define que o valor anterior de previousMilis será removido, e será adicionado o valor atual de millis();
if (isRecording) {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
bool currentState = digitalRead(17); // Ler o estado atual de um dos LEDs
digitalWrite(17, !currentState); // Alterna o estado de ambos os LEDs
digitalWrite(30, !currentState);
}
} else if (!isReplaying) {
digitalWrite(17, 1); // Garante que os LEDs estejam apagados durante o uso comum
digitalWrite(30, 1);
}
Em currentState = digitalRead(17), o valor de currentState é definido como o valor do pino digital 17, para que mais abaixo, os valores dos pinos 17 e 30 sejam definidos com o valor de currentstate invertido, ou seja, o valor é definido antes e invertido na saída. Por exemplo, se o valor de pino 17 for 0, abaixo da linha bool currentState = digitalRead(17) o valor que é escrito, será 1. Isso inverte o estado da saída do LED.
millis(): é usado para verificar o tempo atual e alternar os LEDs a cada 500ms, piscando-os durante o processo de gravação.
else if (!isReplaying) - Garante que os leds estejam sempre apagados, durante o uso comum.
Reprodução dos movimentos gravados
Ao pressionar o botão 15, o estado de !isReplaying é alterado. Se a reprodução for iniciada, os servos fazem uma transição suave para a posição inicial e o replayIndex é resetado para 0. Os leds são acesos e os servos vão para a posição inicial suavemente. O delay é adicionado para debouncing no botão.
// Controle de reprodução de movimentos gravados
if (digitalRead(15) == LOW && !isRecording) {
isReplaying = !isReplaying; // Alterna o estado de reprodução
if (isReplaying) {
smoothTransition(pos1[0], pos2[0], pos3[0], pos4[0]); // Transição suave para a posição inicial
replayIndex = 0; // Reseta o índice de reprodução
Serial.println("Iniciando reprodução");
digitalWrite(17, 0); // Acende os LEDs durante a reprodução
digitalWrite(30, 0);
} else {
Serial.println("Reprodução interrompida");
smoothTransition(s1, s2, s3, s4); // Retorna suavemente para a posição atual
digitalWrite(17, 1); // Apaga os LEDs ao interromper a reprodução
digitalWrite(30, 1);
}
delay(500); // Debounce para o botão de reprodução
}
Nessa parte, o isReplaying é verificado para ver se o código está em modo de reprodução. com isso se replayIndex for menor que recordedSteps, as posições dos servo1 a servo4, são escritas com o valor armazenado de pos1[replayIndex] até pos4[replayIndex].
if (isReplaying) {
if (replayIndex < recordedSteps) {
servo1.write(pos1[replayIndex]);
servo2.write(pos2[replayIndex]);
servo3.write(pos3[replayIndex]);
servo4.write(pos4[replayIndex]);
replayIndex++;
delay(30);
} else {
smoothTransition(pos1[0], pos2[0], pos3[0], pos4[0]);
replayIndex = 0;
}
}
Em smoothTransition quando todos os passos são reproduzidos, a transição suave retorna os servos à posição inicial.
Função SmoothTransition
Essa função, tem como objetivo mover os servos das posições atuais para as posições alvo, gradualmente. Ela suaviza a transição para que os movimentos não sejam abruptos.
void smoothTransition(int target1, int target2, int target3, int target4) {
int current1 = servo1.read();
int current2 = servo2.read();
int current3 = servo3.read();
int current4 = servo4.read();
while (current1 != target1 || current2 != target2 || current3 != target3 || current4 != target4) {
if (current1 < target1) current1++;
else if (current1 > target1) current1--;
if (current2 < target2) current2++;
else if (current2 > target2) current2--;
if (current3 < target3) current3++;
else if (current3 > target3) current3--;
if (current4 < target4) current4++;
else if (current4 > target4) current4--;
servo1.write(current1);
servo2.write(current2);
servo3.write(current3);
servo4.write(current4);
delay(25); // Ajuste o delay para controlar a suavidade da transição
}
}
As variáveis current1, current2, current3 e current4 são definidas como as posições atuais dos servos através do servoX.read(). Isso captura a posição atual dos servos antes de iniciar a transição. Com isso o loop while verifica se qualquer um dos servos ainda não atingiu sua posição-alvo. Se a posição atual (current1 a current4) for menor que a posição-alvo (target1 a target 4 ), a posição é incrementada em 1 unidade; se for maior, ela é decrementada.
Esse processo contínuo garante que cada servo se mova gradualmente em direção à sua posição final, sem movimentos bruscos.
A cada iteração do loop, os servos são movidos para suas novas posições incrementadas ou decrementadas através das chamadas para servo1.write(current1), servo2.write(current2), e assim por diante.
O delay(25) é utilizado para tornar a transição mais lenta e suave.
Código completo e comentado:
#include <Servo.h> // Inclui a biblioteca Servo para controlar os servomotores
// Declara quatro objetos Servo para controlar os motores
Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;
// Pinos analógicos aos quais os potenciômetros estão conectados
int potpin1 = A0; // Pino A0 para o potenciômetro 1
int potpin2 = A1; // Pino A1 para o potenciômetro 2
int potpin3 = A3; // Pino A3 para o potenciômetro 3
int potpin4 = A2; // Pino A2 para o potenciômetro 4
// Variáveis para armazenar os valores lidos dos potenciômetros
int val1, val2, val3, val4;
// Ângulos iniciais dos servos
int s1 = 90, s2 = 50, s3 = 10, s4 = 25;
// Definição do número máximo de passos que podem ser gravados
const int maxSteps = 500; // Reduzido para economizar memória
int recordedSteps = 0; // Conta quantos passos foram gravados
// Arrays que armazenam os ângulos dos servos durante a gravação
byte pos1[maxSteps], pos2[maxSteps], pos3[maxSteps], pos4[maxSteps];
// Variáveis de controle para gravação e reprodução
bool isRecording = false; // Indica se está gravando movimentos
bool isReplaying = false; // Indica se está reproduzindo movimentos
int replayIndex = 0; // Índice atual durante a reprodução
// Variáveis para controle de tempo e piscar dos LEDs
unsigned long previousMillis = 0; // Armazena o último tempo de atualização
const long interval = 500; // Intervalo de 500 ms para piscar o LED
void setup() {
// Configura os pinos para os botões de gravação e reprodução como entradas com resistores de pull-up
pinMode(14, INPUT_PULLUP); // Botão de gravação
pinMode(15, INPUT_PULLUP); // Botão de reprodução
// Configura os pinos 17 e 30 como saídas para controlar LEDs indicadores
pinMode(17, OUTPUT); // LED indicador de gravação
pinMode(30, OUTPUT); // LED indicador de reprodução
// Inicializa a comunicação serial a 9600 bps
Serial.begin(9600);
// Anexa os servos aos pinos digitais
servo1.attach(10); // Servo da base, conectado ao pino digital 10
servo2.attach(5); // Servo de extensão, conectado ao pino digital 5
servo3.attach(6); // Servo de altura, conectado ao pino digital 6
servo4.attach(3); // Servo da garra, conectado ao pino digital 3
// Define as posições iniciais dos servos
servo1.write(s1);
servo2.write(s2);
servo3.write(s3);
servo4.write(s4);
// Garante que os LEDs estejam apagados inicialmente
digitalWrite(17, 1); // LED de gravação apagado
digitalWrite(30, 1); // LED de reprodução apagado
}
// Função para fazer a transição suave dos servos para as posições alvo
void smoothTransition(int target1, int target2, int target3, int target4) {
// Lê as posições atuais dos servos
int current1 = servo1.read();
int current2 = servo2.read();
int current3 = servo3.read();
int current4 = servo4.read();
// Enquanto qualquer servo não atingir a posição alvo
while (current1 != target1 || current2 != target2 || current3 != target3 || current4 != target4) {
// Move o servo1 para a posição alvo de forma suave
if (current1 < target1) current1++;
else if (current1 > target1) current1--;
// Move o servo2 para a posição alvo de forma suave
if (current2 < target2) current2++;
else if (current2 > target2) current2--;
// Move o servo3 para a posição alvo de forma suave
if (current3 < target3) current3++;
else if (current3 > target3) current3--;
// Move o servo4 para a posição alvo de forma suave
if (current4 < target4) current4++;
else if (current4 > target4) current4--;
// Define as novas posições dos servos
servo1.write(current1);
servo2.write(current2);
servo3.write(current3);
servo4.write(current4);
delay(25); // Delay para suavizar a transição
}
}
void loop() {
// Se não estiver reproduzindo, permite controle manual e gravação
if (!isReplaying) {
// Lê o valor do potenciômetro conectado ao pino A0
val1 = analogRead(potpin1);
if (val1 < 100) { // Se o valor for muito baixo, diminui o ângulo do servo1
s1 = s1 - 2;
if (s1 <= 10) s1 = 10; // Limita o ângulo mínimo
servo1.write(s1); // Define o novo ângulo para o servo1
delay(25); // Delay para suavizar o movimento
}
if (val1 > 900) { // Se o valor for muito alto, aumenta o ângulo do servo1
s1 = s1 + 2;
if (s1 >= 170) s1 = 170; // Limita o ângulo máximo
servo1.write(s1); // Define o novo ângulo para o servo1
delay(25); // Delay para suavizar o movimento
}
// Controle semelhante para os outros servos (servo2, servo3 e servo4)
// Controle da extensão do braço (servo2)
val2 = analogRead(potpin2);
if (val2 < 100) {
s2 = s2 + 2;
if (s2 >= 140) s2 = 140; // Limita a extensão máxima
if (s2 >= 120 && s3 <= 20) s2 = 120; // Restrição baseada na altura
servo2.write(s2); // Define o novo ângulo para o servo2
delay(25); // Delay para suavizar o movimento
}
if (val2 > 900) {
s2 = s2 - 2;
if (s2 <= 10) s2 = 10; // Limita a extensão mínima
if (s2 >= 120 && s3 <= 20) s2 = 120; // Restrição baseada na altura
servo2.write(s2); // Define o novo ângulo para o servo2
delay(25); // Delay para suavizar o movimento
}
// Controle da altura do braço (servo3)
val3 = analogRead(potpin3);
if (val3 > 900) {
s3 = s3 - 2;
if (s3 <= 10) s3 = 10; // Limita a altura mínima
if (s2 >= 120 && s2 < 130 && s3 <= 20) s3 = 20; // Restrição baseada na extensão
else if (s2 >= 130 && s2 < 140 && s3 <= 30) s3 = 30;
else if (s2 == 140 && s3 <= 65) s3 = 65;
servo3.write(s3); // Define o novo ângulo para o servo3
delay(25); // Delay para suavizar o movimento
}
if (val3 < 100) {
s3 = s3 + 2;
if (s3 >= 170) s3 = 170; // Limita a altura máxima
if (s2 >= 120 && s2 < 130 && s3 <= 20) s3 = 20;
else if (s2 >= 130 && s2 < 140 && s3 <= 30) s3 = 30;
else if (s2 == 140 && s3 <= 65) s3 = 65;
servo3.write(s3); // Define o novo ângulo para o servo3
delay(25); // Delay para suavizar o movimento
}
// Controle da garra (servo4)
val4 = analogRead(potpin4);
if (val4 < 100) {
s4 = s4 + 2;
if (s4 < 0) s4 = 0; // Limita a abertura mínima da garra
servo4.write(s4); // Define o novo ângulo para o servo4
delay(25); // Delay para suavizar o movimento
}
if (val4 > 900) {
s4 = s4 - 2;
if (s4 > 30) s4 = 30; // Limita a abertura máxima da garra
if (s4 < 0) s4 = 0; // Evita valores negativos
servo4.write(s4); // Define o novo ângulo para o servo4
delay(25); // Delay para suavizar o movimento
}
// Gravação de movimentos
if (isRecording && recordedSteps < maxSteps) {
// Armazena as posições atuais dos servos nos arrays de gravação
pos1[recordedSteps] = s1;
pos2[recordedSteps] = s2;
pos3[recordedSteps] = s3;
pos4[recordedSteps] = s4;
recordedSteps++; // Incrementa o contador de passos gravados
delay(25); // Delay para capturar os movimentos de forma controlada
}
}
// Controle de início e parada de gravação
if (digitalRead(14) == LOW && !isReplaying) {
isRecording = !isRecording; // Alterna o estado de gravação
if (!isRecording) {
Serial.println("Gravação finalizada");
digitalWrite(17, 1); // Apaga o LED de gravação
digitalWrite(30, 1); // Apaga o LED de gravação
} else {
recordedSteps = 0; // Reseta o contador de gravação
Serial.println("Gravação iniciada");
}
delay(500); // Debounce para o botão de gravação
}
// Piscar LEDs enquanto grava
if (isRecording) {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis; // Atualiza o tempo anterior
bool currentState = digitalRead(17); // Lê o estado atual do LED
digitalWrite(17, !currentState); // Alterna o estado do LED
digitalWrite(30, !currentState); // Alterna o estado do outro LED
}
} else if (!isReplaying) {
digitalWrite(17, 1); // Garante que os LEDs estejam apagados quando não está gravando ou reproduzindo
digitalWrite(30, 1);
}
// Controle de reprodução de movimentos gravados
if (digitalRead(15) == LOW && !isRecording) {
isReplaying = !isReplaying; // Alterna o estado de reprodução
if (isReplaying) {
smoothTransition(pos1[0], pos2[0], pos3[0], pos4[0]); // Transição suave para a posição inicial
replayIndex = 0; // Reseta o índice de reprodução
Serial.println("Iniciando reprodução");
digitalWrite(17, 0); // Acende os LEDs durante a reprodução
digitalWrite(30, 0);
} else {
Serial.println("Reprodução interrompida");
smoothTransition(s1, s2, s3, s4); // Retorna suavemente para a posição atual
digitalWrite(17, 1); // Apaga os LEDs ao interromper a reprodução
digitalWrite(30, 1);
}
delay(500); // Debounce para o botão de reprodução
}
// Se estiver no modo de reprodução
if (isReplaying) {
if (replayIndex < recordedSteps) {
// Define as posições dos servos de acordo com os valores gravados
servo1.write(pos1[replayIndex]);
servo2.write(pos2[replayIndex]);
servo3.write(pos3[replayIndex]);
servo4.write(pos4[replayIndex]);
replayIndex++; // Incrementa o índice de reprodução
delay(30); // Delay para controlar a velocidade de reprodução
} else {
// Retorna suavemente à primeira posição gravada
smoothTransition(pos1[0], pos2[0], pos3[0], pos4[0]); // Suave retorno à posição inicial
replayIndex = 0; // Reinicia o índice para repetir o loop de reprodução
}
}
}
Video do braço robótico funcionando
Veja abaixo, um video, demonstrando o braço robótico funcionando, e seus modos de gravação e reprodução.
Com isso, hoje aprendemos a programar completamente o braço robótico. Fique atento para mais tutoriais em nosso blog.