Início›Projetos›Braço Servo
⬤ Projeto Integrado · LCD + 2 Servos

🦾 Braço Servo com Interface LCD

⏱️ 50–60 min 🧩 2× Servo · LCD 20×4 · Joystick · Chars custom · Modo MANUAL/SEQUÊNCIA

Nesta página

🎯 Objetivo e Resultado

Mini-braço robótico com duas juntas de servo. O joystick do Kit B 37 controla cada eixo em tempo real (modo MANUAL). O botão SW do joystick alterna para modo SEQUÊNCIA que reproduz 5 posições pré-gravadas. O LCD exibe o ângulo de cada junta, o modo activo e uma barra gráfica de posição.

Braco Servo PR.FT9_5J1: 090° J2: 045° [████████ ]Modo: MANUAL

🔌 Ligações

ComponentePino ArduinoNota
LCD 20×4 (I2C)A4 (SDA) / A5 (SCL)Endereço 0x27
Servo 1 — junta baseD9 (PWM 50 Hz)SG90 ou MG90S
Servo 2 — junta braçoD10 (PWM 50 Hz)SG90 ou MG90S
Joystick VRx (eixo X)A0Controla Servo 1
Joystick VRy (eixo Y)A1Controla Servo 2
Joystick SW (botão)D2 (INPUT_PULLUP)Alterna MANUAL/SEQUÊNCIA
⚠️
Dois servos SG90 em simultâneo consomem até 1A em carga máxima. Se o Arduino reiniciar ao mover os servos, alimenta os servos por fonte externa de 5V com GND comum ao Arduino.

🔨 Fases de Montagem

Fase 1 — Dois servos centralizados

Liga D9 e D10. Carrega um sketch simples: sv1.write(90); sv2.write(90); — os dois servos vão para o centro. Confirma que o braço está na posição neutra antes de continuar.

Fase 2 — Joystick para controlo manual

Lê X→ sv1, Y→ sv2 com map(0,1023,0,180). Adiciona zona morta de ±80 em redor do centro. O LCD actualiza sem flickering comparando com os últimos ângulos.

Fase 3 — Modo SEQUÊNCIA

Define 5 posições em struct. Ao premir SW, alterna para sequência que cicla pelas posições com delay configurável. Novo premir de SW regressa a MANUAL.

Fase 4 — Chars custom e dashboard

Regista ° (slot 4) e bloco para barra (slot 5). Integra tudo no dashboard das 4 linhas. Testa MANUAL ↔ SEQUÊNCIA com o joystick.

💻 Código Completo

proj_braco_servo.ino
#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20, 4);
Servo sv1, sv2;

#define JX   A0
#define JY   A1
#define JSW  2
#define DZ   80   // zona morta do joystick

// ── Chars custom ────────────────────────────────────────────
byte ic_grau[8]  = {0b00110,0b01001,0b01001,0b00110,0b00000,0b00000,0b00000,0b00000};
byte ic_bloco[8] = {0b11111,0b11111,0b11111,0b11111,0b11111,0b11111,0b11111,0b11111};

// ── Estado ──────────────────────────────────────────────────
int  a1 = 90, a2 = 90;
int  ult1 = -1, ult2 = -1;
bool modoSeq = false;
int  seqIdx  = 0;
unsigned long tSW = 0, tSeq = 0;

// ── Sequência de 5 posições (junta1, junta2, pausa ms) ──────
struct PosSeq { int a1, a2, ms; const char* nome; };
const PosSeq seq[5] = {
  {  0,  90, 1200, "Inicio    "},
  { 45,  45,  900, "Baixo Esq"},
  { 90, 135, 1000, "Centro Cim"},
  {135,  45,  900, "Alto Dir  "},
  {180,  90, 1200, "Fim curso "},
};

// ── Dashboard LCD ────────────────────────────────────────────
void dashboard(const char* modo) {
  // Só redesenha quando os ângulos mudam
  if (a1 != ult1 || a2 != ult2) {
    lcd.setCursor(0, 1);
    lcd.print("J1:"); lcd.print(a1);
    lcd.write(4);  // °
    lcd.print("  J2:"); lcd.print(a2);
    lcd.write(4); lcd.print("  ");

    // Linha 2 — barra de J1 (0–180 → 0–20 blocos)
    lcd.setCursor(0, 2); lcd.print("[");
    int b = a1 * 18 / 180;
    for (int i=0; i<18; i++) i < b ? lcd.write(5) : lcd.write(32);
    lcd.print("]");

    ult1 = a1; ult2 = a2;
  }
  lcd.setCursor(0, 3);
  lcd.print("Modo: "); lcd.print(modo); lcd.print("  ");
}

void setup() {
  lcd.createChar(4, ic_grau);
  lcd.createChar(5, ic_bloco);
  lcd.init(); lcd.backlight();
  sv1.attach(9);  sv1.write(90);
  sv2.attach(10); sv2.write(90);
  pinMode(JSW, INPUT_PULLUP);
  lcd.setCursor(0,0); lcd.print("Braco Servo PR.FT9_5");
}

void loop() {
  // Botão SW — alterna modo
  if (digitalRead(JSW)==LOW && millis()-tSW>400) {
    modoSeq=!modoSeq; seqIdx=0; tSW=millis();
    lcd.setCursor(0,3);
    lcd.print(modoSeq ? "Modo: SEQUENCIA    " : "Modo: MANUAL       ");
  }

  if (!modoSeq) {
    // ── MODO MANUAL — joystick ──────────────────────────────
    int dx = analogRead(JX) - 512;
    int dy = analogRead(JY) - 512;
    if (dx >  DZ) a1 = constrain(a1 + 2, 0, 180);
    if (dx < -DZ) a1 = constrain(a1 - 2, 0, 180);
    if (dy >  DZ) a2 = constrain(a2 + 2, 0, 180);
    if (dy < -DZ) a2 = constrain(a2 - 2, 0, 180);
    sv1.write(a1); sv2.write(a2);
    dashboard("MANUAL    ");
    delay(20);
  } else {
    // ── MODO SEQUÊNCIA — cicla pelas 5 posições ─────────────
    const PosSeq& p = seq[seqIdx];
    a1 = p.a1; a2 = p.a2;
    sv1.write(a1); sv2.write(a2);
    dashboard(p.nome);
    delay(p.ms);
    seqIdx = (seqIdx + 1) % 5;
  }
}

🚀 Como Expandir

🏆 Para ir mais longe

PR.FT9_5 · LCD 20×4 · L293D Motor Driver · Arduino UNO Rev3 · Início · led.dge.medu.pt