controleIntermediárioArduinoPWMMotores DCL298NH-BridgeControle

Controlador PWM para Motores DC com Arduino e L298N

Sistema completo de controle de velocidade e direção para motores DC usando PWM, com interface potenciômetro, controle remoto e proteções de segurança.

25/07/2025
3-4 horas
R$ 85-120
Fixtron Circuits

🔧 Componentes Principais

Arduino Uno R3Módulo L298N DriverMotores DC 12VPotenciômetros 10kΩDisplay OLED 128x64Encoder rotativoFonte 12V 3A

🛠️ Ferramentas Necessárias

Arduino IDEMultímetroOsciloscópioFerro de solda

Controlador PWM para Motores DC com Arduino e L298N

Construa um sistema avançado de controle de motores DC com modulação PWM, permitindo controle preciso de velocidade e direção, interface gráfica em display OLED, múltiplos modos de operação e proteções de segurança integradas.

🎯 O que você vai construir

Funcionalidades Principais:

  • Controle PWM de 0-100% para 2 motores independentes
  • Controle de direção com H-Bridge L298N
  • Interface OLED com informações em tempo real
  • Múltiplos modos: Manual, automático, jogging
  • Encoder rotativo para ajuste fino de velocidade
  • Proteção térmica e sobrecorrente
  • Rampa de aceleração configurável
  • Feedback de RPM com sensor Hall
  • Controle remoto via Bluetooth/WiFi

Modos de Operação:

  • 🎛️ Manual: Controle direto por potenciômetro
  • 🤖 Automático: Perfis pré-programados
  • 🔄 Jogging: Pulsos controlados para posicionamento
  • 📊 Diagnóstico: Monitoramento de parâmetros
  • 🛡️ Segurança: Parada de emergência

📋 Lista de Materiais

Componentes Eletrônicos

| Item | Quantidade | Preço Aprox. | Especificações | |------|------------|--------------|----------------| | Arduino Uno R3 | 1 | R$ 35,00 | ATmega328P | | Módulo L298N | 1 | R$ 18,00 | Dual H-Bridge | | Motor DC 12V | 2 | R$ 25,00 | 100-300 RPM, 2A | | Display OLED 128x64 | 1 | R$ 22,00 | I2C interface | | Encoder rotativo KY-040 | 1 | R$ 8,00 | Com push button | | Potenciômetro 10kΩ | 2 | R$ 6,00 | Linear | | Sensor Hall A3144 | 2 | R$ 4,00 | Para feedback RPM | | Fonte 12V 3A | 1 | R$ 28,00 | Chaveada | | Módulo Bluetooth HC-05 | 1 | R$ 15,00 | Para controle remoto | | Push buttons | 4 | R$ 4,00 | Momentâneos | | Resistores diversos | 1 lote | R$ 5,00 | 330Ω, 10kΩ, etc | | Capacitores 100µF | 2 | R$ 3,00 | Para filtros | | LEDs indicadores | 6 | R$ 3,00 | Verde, vermelho, azul | | Placa perfurada | 1 | R$ 8,00 | 10x15cm | | Conectores terminais | 4 | R$ 6,00 | Para motores | | Caixa plástica | 1 | R$ 18,00 | 20x15x8cm | | Total | | R$ 208,00 | |

Ferramentas Necessárias

  • 🔧 Arduino IDE (software)
  • 🔌 Multímetro digital
  • 📊 Osciloscópio (recomendado)
  • ⚡ Ferro de solda 40W
  • 🪛 Chaves de fenda variadas
  • 📏 Régua e estilete
  • 🔍 Lupa para soldagem

⚙️ Fundamentos Técnicos

PWM (Pulse Width Modulation)

O controle PWM funciona variando a largura dos pulsos mantendo a frequência constante:

Duty Cycle = (Ton / Tperiod) × 100%
Tensão Média = Vin × Duty Cycle

Frequências típicas:

  • Arduino: 490 Hz / 980 Hz (pinos 5,6)
  • Motores DC: 1-20 kHz (ideal: 4-16 kHz)
  • Áudio: >20 kHz (para evitar ruído audível)

H-Bridge L298N

Circuito que permite controle bidirecional:

Configuração H-Bridge:
IN1 | IN2 | Função
----|-----|--------
 0  |  0  | Parado (frenagem)
 0  |  1  | Sentido anti-horário
 1  |  0  | Sentido horário
 1  |  1  | Parado (frenagem)

Cálculos de Potência

// Potência dissipada no L298N
P_dissipated = I_motor² × R_internal + V_drop × I_motor

// Eficiência do sistema
Efficiency = (P_motor / P_input) × 100%

// Torque vs Velocidade
Torque = K_t × I_motor
Speed = (V_motor - I_motor × R_motor) / K_v

🔌 Esquema de Ligação

Diagrama Principal

                    CONTROLADOR PWM MOTORES DC
┌─────────────────────────────────────────────────────────────────┐
│  ╭──────────╮     ╭──────────╮     ╭──────────────────╮        │
│  │ Fonte 12V│────▶│ L298N    │────▶│    Motor A       │        │
│  │ 3A       │     │ Driver   │     │    (12V, 2A)     │        │
│  ╰──────────╯     ╰─────┬────╯     ╰──────────────────╯        │
│                         │                                       │
│                         │          ╭──────────────────╮        │
│                         └─────────▶│    Motor B       │        │
│                                    │    (12V, 2A)     │        │
│  ╭─────────────╮                   ╰──────────────────╯        │
│  │ Arduino Uno │                                               │
│  │             │◀──────╭──────────╮                           │
│  │ PWM: 5,6,9,10│       │ Encoder  │                           │
│  │ I2C: A4,A5  │       │ Rotativo │                           │
│  │ ADC: A0,A1  │       ╰──────────╯                           │
│  ╰─────────────╯                                               │
│         │                                                      │
│         │              ╭──────────────╮                       │
│         └─────────────▶│ Display OLED │                       │
│                        │ 128x64 I2C   │                       │
│  ╭───────────╮         ╰──────────────╯                       │
│  │Potenciôm. │                                                │
│  │Velocidade │         ╭──────────────╮                       │
│  ╰───────────╯        │ Módulo       │                       │
│                       │ Bluetooth    │                       │
│  ╭───────────╮        │ HC-05        │                       │
│  │Potenciôm. │        ╰──────────────╯                       │
│  │Direção    │                                               │
│  ╰───────────╯        ╭──────────────╮                       │
│                       │ Sensores Hall│                       │
│  ╭───────────╮        │ Feedback RPM │                       │
│  │ Push      │        ╰──────────────╯                       │
│  │ Buttons   │                                               │
│  ╰───────────╯                                               │
└─────────────────────────────────────────────────────────────────┘

Pinagem Detalhada

Arduino Uno:

// Controle L298N
#define ENA_PIN     5   // PWM Motor A (490 Hz)
#define IN1_PIN     7   // Direção Motor A
#define IN2_PIN     8   // Direção Motor A
#define ENB_PIN     6   // PWM Motor B (490 Hz)
#define IN3_PIN     9   // Direção Motor B
#define IN4_PIN     10  // Direção Motor B

// Interface do usuário
#define POT_SPEED   A0  // Potenciômetro velocidade
#define POT_DIR     A1  // Potenciômetro direção
#define ENC_CLK     2   // Encoder CLK (interrupt)
#define ENC_DT      3   // Encoder DT (interrupt)
#define ENC_SW      4   // Encoder push button

// Display e comunicação
#define SDA_PIN     A4  // I2C Data (OLED)
#define SCL_PIN     A5  // I2C Clock (OLED)
#define BT_TX       11  // Bluetooth TX
#define BT_RX       12  // Bluetooth RX

// Sensores e botões
#define HALL_A      A2  // Sensor Hall Motor A
#define HALL_B      A3  // Sensor Hall Motor B
#define BTN_START   13  // Botão start/stop
#define BTN_MODE    A6  // Botão mudança modo
#define BTN_EMERGENCY A7 // Botão emergência

💻 Código Principal Arduino

/*
  Controlador PWM para Motores DC
  
  Sistema avançado de controle de motores DC com:
  - Controle PWM de velocidade e direção
  - Interface OLED com menu interativo
  - Múltiplos modos de operação
  - Proteções de segurança
  - Feedback de RPM
  - Controle remoto Bluetooth
  
  Autor: Fixtron Circuits
  Data: Julho 2025
  Versão: 2.1
*/

#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <SoftwareSerial.h>
#include <EEPROM.h>

// Configurações do display
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Configuração Bluetooth
SoftwareSerial bluetooth(12, 11); // RX, TX

// Definições de pinos
#define ENA_PIN     5
#define IN1_PIN     7
#define IN2_PIN     8
#define ENB_PIN     6
#define IN3_PIN     9
#define IN4_PIN     10

#define POT_SPEED   A0
#define POT_DIR     A1
#define ENC_CLK     2
#define ENC_DT      3
#define ENC_SW      4

#define HALL_A      A2
#define HALL_B      A3
#define BTN_START   13
#define BTN_MODE    A6
#define BTN_EMERGENCY A7

// Estrutura para configurações dos motores
struct MotorConfig {
  int pwmPin;
  int dir1Pin;
  int dir2Pin;
  int hallPin;
  int pwmValue;
  int direction;  // 0=parado, 1=horário, -1=anti-horário
  unsigned long rpmCount;
  unsigned long lastRpmTime;
  float currentRPM;
  bool enabled;
};

MotorConfig motorA = {ENA_PIN, IN1_PIN, IN2_PIN, HALL_A, 0, 0, 0, 0, 0.0, true};
MotorConfig motorB = {ENB_PIN, IN3_PIN, IN4_PIN, HALL_B, 0, 0, 0, 0, 0.0, true};

// Variáveis de controle
enum OperationMode {
  MODE_MANUAL,
  MODE_AUTO,
  MODE_JOGGING,
  MODE_DIAGNOSTIC
};

OperationMode currentMode = MODE_MANUAL;
bool systemEnabled = false;
bool emergencyStop = false;

// Parâmetros configuráveis
struct SystemConfig {
  int maxPWM = 255;
  int rampRate = 5;          // PWM/loop para rampa suave
  int pwmFrequency = 490;    // Hz
  float maxRPM = 300.0;      // RPM máximo
  int autoSequence = 0;      // Sequência automática atual
  bool protectionEnabled = true;
} config;

// Variáveis do encoder
volatile int encoderPosition = 0;
volatile bool encoderPressed = false;
int lastEncoderPosition = 0;

// Menu e interface
int menuSelection = 0;
int displayMode = 0;
unsigned long lastDisplayUpdate = 0;
unsigned long lastButtonCheck = 0;

void setup() {
  Serial.begin(9600);
  bluetooth.begin(9600);
  
  // Inicializar display
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("Falha no display OLED"));
    while(1);
  }
  
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println(F("Controlador PWM"));
  display.println(F("Motores DC v2.1"));
  display.println(F(""));
  display.println(F("Inicializando..."));
  display.display();
  delay(2000);
  
  // Configurar pinos
  setupPins();
  
  // Configurar interrupções
  attachInterrupt(digitalPinToInterrupt(ENC_CLK), encoderISR, CHANGE);
  attachInterrupt(digitalPinToInterrupt(ENC_DT), encoderISR, CHANGE);
  
  // Configurar PWM frequency (Timer 0 - pins 5,6)
  TCCR0B = TCCR0B & B11111000 | B00000011; // 490 Hz
  
  // Carregar configurações da EEPROM
  loadConfig();
  
  // Teste inicial dos motores
  motorTest();
  
  Serial.println(F("Sistema inicializado!"));
  updateDisplay();
}

void loop() {
  // Verificar botões e encoder
  checkInputs();
  
  // Processar comandos Bluetooth
  processBluetoothCommands();
  
  // Executar modo atual
  switch(currentMode) {
    case MODE_MANUAL:
      manualMode();
      break;
    case MODE_AUTO:
      automaticMode();
      break;
    case MODE_JOGGING:
      joggingMode();
      break;
    case MODE_DIAGNOSTIC:
      diagnosticMode();
      break;
  }
  
  // Calcular RPM
  calculateRPM();
  
  // Verificar proteções
  checkProtections();
  
  // Atualizar display
  if(millis() - lastDisplayUpdate > 100) {
    updateDisplay();
    lastDisplayUpdate = millis();
  }
  
  delay(10); // Loop principal a ~100Hz
}

void setupPins() {
  // Pinos de controle do motor
  pinMode(ENA_PIN, OUTPUT);
  pinMode(ENB_PIN, OUTPUT);
  pinMode(IN1_PIN, OUTPUT);
  pinMode(IN2_PIN, OUTPUT);
  pinMode(IN3_PIN, OUTPUT);
  pinMode(IN4_PIN, OUTPUT);
  
  // Pinos de entrada
  pinMode(POT_SPEED, INPUT);
  pinMode(POT_DIR, INPUT);
  pinMode(ENC_SW, INPUT_PULLUP);
  pinMode(BTN_START, INPUT_PULLUP);
  pinMode(BTN_MODE, INPUT_PULLUP);
  pinMode(BTN_EMERGENCY, INPUT_PULLUP);
  pinMode(HALL_A, INPUT);
  pinMode(HALL_B, INPUT);
  
  // Estado inicial - motores parados
  stopAllMotors();
}

void encoderISR() {
  static unsigned long lastInterrupt = 0;
  unsigned long currentTime = millis();
  
  // Debounce
  if(currentTime - lastInterrupt < 5) return;
  lastInterrupt = currentTime;
  
  int clkState = digitalRead(ENC_CLK);
  int dtState = digitalRead(ENC_DT);
  
  if(clkState != dtState) {
    encoderPosition++;
  } else {
    encoderPosition--;
  }
}

void manualMode() {
  if(!systemEnabled) return;
  
  // Ler potenciômetros
  int speedPot = analogRead(POT_SPEED);
  int dirPot = analogRead(POT_DIR);
  
  // Calcular velocidade (0-255)
  int targetSpeed = map(speedPot, 0, 1023, 0, config.maxPWM);
  
  // Calcular direção baseada no potenciômetro
  int direction = 0;
  if(dirPot < 400) {
    direction = -1; // Anti-horário
  } else if(dirPot > 600) {
    direction = 1;  // Horário
  }
  
  // Aplicar rampa suave
  applyRamp(&motorA, targetSpeed, direction);
  applyRamp(&motorB, targetSpeed, direction);
  
  // Atualizar motores
  updateMotor(&motorA);
  updateMotor(&motorB);
}

void automaticMode() {
  static unsigned long lastSequenceChange = 0;
  static int sequenceStep = 0;
  
  if(!systemEnabled) return;
  
  // Sequências automáticas pré-programadas
  if(millis() - lastSequenceChange > 3000) { // Mudança a cada 3s
    switch(sequenceStep) {
      case 0:
        setMotorTarget(&motorA, 128, 1);
        setMotorTarget(&motorB, 128, 1);
        break;
      case 1:
        setMotorTarget(&motorA, 255, 1);
        setMotorTarget(&motorB, 255, 1);
        break;
      case 2:
        setMotorTarget(&motorA, 128, -1);
        setMotorTarget(&motorB, 128, -1);
        break;
      case 3:
        setMotorTarget(&motorA, 0, 0);
        setMotorTarget(&motorB, 0, 0);
        break;
    }
    
    sequenceStep = (sequenceStep + 1) % 4;
    lastSequenceChange = millis();
  }
  
  // Aplicar mudanças com rampa
  applyRamp(&motorA, motorA.pwmValue, motorA.direction);
  applyRamp(&motorB, motorB.pwmValue, motorB.direction);
  
  updateMotor(&motorA);
  updateMotor(&motorB);
}

void joggingMode() {
  // Modo jogging - pulsos controlados para posicionamento preciso
  static bool jogActive = false;
  static unsigned long jogStartTime = 0;
  static int jogDuration = 100; // ms
  
  // Controle via encoder
  int encoderDelta = encoderPosition - lastEncoderPosition;
  lastEncoderPosition = encoderPosition;
  
  if(encoderDelta != 0 && !jogActive) {
    // Iniciar pulso de jog
    jogActive = true;
    jogStartTime = millis();
    
    int direction = (encoderDelta > 0) ? 1 : -1;
    int speed = min(128, abs(encoderDelta) * 32); // Velocidade baseada na rotação
    
    setMotorTarget(&motorA, speed, direction);
    setMotorTarget(&motorB, speed, direction);
    updateMotor(&motorA);
    updateMotor(&motorB);
  }
  
  // Parar após duração do jog
  if(jogActive && (millis() - jogStartTime > jogDuration)) {
    jogActive = false;
    setMotorTarget(&motorA, 0, 0);
    setMotorTarget(&motorB, 0, 0);
    updateMotor(&motorA);
    updateMotor(&motorB);
  }
}

void diagnosticMode() {
  // Modo diagnóstico - monitoramento detalhado
  static unsigned long lastDiagUpdate = 0;
  
  if(millis() - lastDiagUpdate > 1000) {
    Serial.println(F("=== DIAGNÓSTICO ==="));
    Serial.print(F("Motor A - PWM: ")); Serial.print(motorA.pwmValue);
    Serial.print(F(", RPM: ")); Serial.print(motorA.currentRPM);
    Serial.print(F(", Dir: ")); Serial.println(motorA.direction);
    
    Serial.print(F("Motor B - PWM: ")); Serial.print(motorB.pwmValue);
    Serial.print(F(", RPM: ")); Serial.print(motorB.currentRPM);
    Serial.print(F(", Dir: ")); Serial.println(motorB.direction);
    
    Serial.print(F("Tensão: ")); Serial.print(readVoltage());
    Serial.print(F("V, Temp: ")); Serial.print(readTemperature());
    Serial.println(F("°C"));
    
    lastDiagUpdate = millis();
  }
}

void applyRamp(MotorConfig* motor, int targetPWM, int targetDir) {
  if(!motor->enabled) return;
  
  // Aplicar rampa suave na velocidade
  if(motor->pwmValue < targetPWM) {
    motor->pwmValue = min(targetPWM, motor->pwmValue + config.rampRate);
  } else if(motor->pwmValue > targetPWM) {
    motor->pwmValue = max(targetPWM, motor->pwmValue - config.rampRate);
  }
  
  // Mudar direção apenas quando parado (segurança)
  if(motor->pwmValue == 0) {
    motor->direction = targetDir;
  }
}

void updateMotor(MotorConfig* motor) {
  if(emergencyStop || !motor->enabled) {
    analogWrite(motor->pwmPin, 0);
    digitalWrite(motor->dir1Pin, LOW);
    digitalWrite(motor->dir2Pin, LOW);
    return;
  }
  
  // Aplicar PWM
  analogWrite(motor->pwmPin, motor->pwmValue);
  
  // Configurar direção
  switch(motor->direction) {
    case 1:  // Horário
      digitalWrite(motor->dir1Pin, HIGH);
      digitalWrite(motor->dir2Pin, LOW);
      break;
    case -1: // Anti-horário
      digitalWrite(motor->dir1Pin, LOW);
      digitalWrite(motor->dir2Pin, HIGH);
      break;
    default: // Parado
      digitalWrite(motor->dir1Pin, LOW);
      digitalWrite(motor->dir2Pin, LOW);
      break;
  }
}

void calculateRPM() {
  static unsigned long lastRpmCalc = 0;
  
  if(millis() - lastRpmCalc > 100) { // Calcular a cada 100ms
    calculateMotorRPM(&motorA);
    calculateMotorRPM(&motorB);
    lastRpmCalc = millis();
  }
}

void calculateMotorRPM(MotorConfig* motor) {
  static int lastHallState[2] = {0, 0};
  int motorIndex = (motor == &motorA) ? 0 : 1;
  
  int hallState = digitalRead(motor->hallPin);
  
  // Detectar transição (pulso)
  if(hallState != lastHallState[motorIndex]) {
    if(hallState == HIGH) {
      motor->rpmCount++;
      
      unsigned long currentTime = millis();
      unsigned long timeDiff = currentTime - motor->lastRpmTime;
      
      if(timeDiff > 0) {
        // RPM = (pulsos/min) * 60 / pulsos_por_revolução
        // Assumindo 1 pulso por revolução
        motor->currentRPM = (60000.0 / timeDiff);
        motor->lastRpmTime = currentTime;
      }
    }
    lastHallState[motorIndex] = hallState;
  }
  
  // Timeout para RPM zero
  if(millis() - motor->lastRpmTime > 2000) {
    motor->currentRPM = 0.0;
  }
}

void checkInputs() {
  if(millis() - lastButtonCheck < 50) return; // Debounce
  lastButtonCheck = millis();
  
  // Botão start/stop
  if(digitalRead(BTN_START) == LOW) {
    systemEnabled = !systemEnabled;
    if(!systemEnabled) stopAllMotors();
    delay(200); // Debounce adicional
  }
  
  // Botão modo
  if(digitalRead(BTN_MODE) == LOW) {
    currentMode = (OperationMode)((currentMode + 1) % 4);
    delay(200);
  }
  
  // Botão emergência
  if(digitalRead(BTN_EMERGENCY) == LOW) {
    emergencyStop = true;
    stopAllMotors();
    systemEnabled = false;
  }
  
  // Encoder button
  if(digitalRead(ENC_SW) == LOW) {
    encoderPressed = true;
    delay(200);
  }
}

void processBluetoothCommands() {
  if(bluetooth.available()) {
    String command = bluetooth.readStringUntil('\n');
    command.trim();
    
    if(command.startsWith("PWM_A:")) {
      int pwm = command.substring(6).toInt();
      setMotorTarget(&motorA, constrain(pwm, 0, 255), motorA.direction);
    }
    else if(command.startsWith("PWM_B:")) {
      int pwm = command.substring(6).toInt();
      setMotorTarget(&motorB, constrain(pwm, 0, 255), motorB.direction);
    }
    else if(command.startsWith("DIR_A:")) {
      int dir = command.substring(6).toInt();
      motorA.direction = constrain(dir, -1, 1);
    }
    else if(command.startsWith("DIR_B:")) {
      int dir = command.substring(6).toInt();
      motorB.direction = constrain(dir, -1, 1);
    }
    else if(command == "START") {
      systemEnabled = true;
      emergencyStop = false;
    }
    else if(command == "STOP") {
      systemEnabled = false;
      stopAllMotors();
    }
    else if(command == "STATUS") {
      sendStatus();
    }
  }
}

void sendStatus() {
  bluetooth.print("MOTOR_A:");
  bluetooth.print(motorA.pwmValue);
  bluetooth.print(",");
  bluetooth.print(motorA.currentRPM);
  bluetooth.print(",");
  bluetooth.println(motorA.direction);
  
  bluetooth.print("MOTOR_B:");
  bluetooth.print(motorB.pwmValue);
  bluetooth.print(",");
  bluetooth.print(motorB.currentRPM);
  bluetooth.print(",");
  bluetooth.println(motorB.direction);
  
  bluetooth.print("SYSTEM:");
  bluetooth.print(systemEnabled ? "ON" : "OFF");
  bluetooth.print(",");
  bluetooth.print(currentMode);
  bluetooth.print(",");
  bluetooth.println(emergencyStop ? "EMERGENCY" : "OK");
}

void updateDisplay() {
  display.clearDisplay();
  display.setCursor(0, 0);
  display.setTextSize(1);
  
  // Header
  display.print(F("PWM Controller "));
  display.println(systemEnabled ? F("ON") : F("OFF"));
  
  // Modo atual
  display.print(F("Modo: "));
  switch(currentMode) {
    case MODE_MANUAL: display.println(F("Manual")); break;
    case MODE_AUTO: display.println(F("Auto")); break;
    case MODE_JOGGING: display.println(F("Jogging")); break;
    case MODE_DIAGNOSTIC: display.println(F("Diagnostic")); break;
  }
  
  // Status dos motores
  display.println(F(""));
  display.print(F("A: PWM=")); display.print(motorA.pwmValue);
  display.print(F(" RPM=")); display.println((int)motorA.currentRPM);
  
  display.print(F("B: PWM=")); display.print(motorB.pwmValue);
  display.print(F(" RPM=")); display.println((int)motorB.currentRPM);
  
  // Informações adicionais
  display.println(F(""));
  if(emergencyStop) {
    display.println(F("** EMERGENCIA **"));
  } else {
    display.print(F("Temp: ")); display.print((int)readTemperature());
    display.print(F("C V: ")); display.print(readVoltage(), 1);
    display.println(F("V"));
  }
  
  display.display();
}

float readTemperature() {
  // Leitura simulada - em projeto real usar sensor de temperatura
  return 25.0 + (analogRead(A6) * 0.01);
}

float readVoltage() {
  // Leitura de tensão através de divisor resistivo
  // Assumindo divisor 3:1 (15V max -> 5V Arduino)
  int adcValue = analogRead(A7);
  return (adcValue / 1023.0) * 5.0 * 3.0;
}

void checkProtections() {
  if(!config.protectionEnabled) return;
  
  float voltage = readVoltage();
  float temperature = readTemperature();
  
  // Proteção por sobretensão (>15V)
  if(voltage > 15.0) {
    emergencyStop = true;
    Serial.println(F("ERRO: Sobretensão detectada!"));
  }
  
  // Proteção por subtensão (<9V)
  if(voltage < 9.0 && systemEnabled) {
    emergencyStop = true;
    Serial.println(F("ERRO: Subtensão detectada!"));
  }
  
  // Proteção térmica (>60°C)
  if(temperature > 60.0) {
    emergencyStop = true;
    Serial.println(F("ERRO: Superaquecimento!"));
  }
  
  // Proteção por RPM excessivo
  if(motorA.currentRPM > config.maxRPM || motorB.currentRPM > config.maxRPM) {
    emergencyStop = true;
    Serial.println(F("ERRO: RPM excessivo!"));
  }
}

void setMotorTarget(MotorConfig* motor, int pwm, int direction) {
  motor->pwmValue = constrain(pwm, 0, config.maxPWM);
  motor->direction = constrain(direction, -1, 1);
}

void stopAllMotors() {
  setMotorTarget(&motorA, 0, 0);
  setMotorTarget(&motorB, 0, 0);
  updateMotor(&motorA);
  updateMotor(&motorB);
}

void motorTest() {
  Serial.println(F("Iniciando teste de motores..."));
  
  // Teste sequencial dos motores
  for(int i = 0; i < 2; i++) {
    MotorConfig* motor = (i == 0) ? &motorA : &motorB;
    
    Serial.print(F("Testando motor ")); 
    Serial.println((i == 0) ? 'A' : 'B');
    
    // Teste horário
    setMotorTarget(motor, 100, 1);
    updateMotor(motor);
    delay(1000);
    
    // Teste anti-horário
    setMotorTarget(motor, 100, -1);
    updateMotor(motor);
    delay(1000);
    
    // Parar
    setMotorTarget(motor, 0, 0);
    updateMotor(motor);
    delay(500);
  }
  
  Serial.println(F("Teste concluído!"));
}

void loadConfig() {
  // Carregar configurações da EEPROM
  int signature = EEPROM.read(0) | (EEPROM.read(1) << 8);
  
  if(signature == 0xABCD) {
    // Configurações válidas encontradas
    EEPROM.get(2, config);
    Serial.println(F("Configurações carregadas da EEPROM"));
  } else {
    // Usar configurações padrão
    saveConfig();
    Serial.println(F("Usando configurações padrão"));
  }
}

void saveConfig() {
  // Salvar configurações na EEPROM
  EEPROM.write(0, 0xCD);
  EEPROM.write(1, 0xAB);
  EEPROM.put(2, config);
  Serial.println(F("Configurações salvas na EEPROM"));
}

🔧 Interface de Controle

App Android com MIT App Inventor

Crie um aplicativo simples para controle remoto:

// Comandos Bluetooth para o app
function sendPWMCommand(motor, value) {
  bluetooth.send(`PWM_${motor}:${value}\n`);
}

function sendDirectionCommand(motor, direction) {
  bluetooth.send(`DIR_${motor}:${direction}\n`);
}

function startSystem() {
  bluetooth.send("START\n");
}

function stopSystem() {
  bluetooth.send("STOP\n");
}

function requestStatus() {
  bluetooth.send("STATUS\n");
}

Interface Web (opcional)

<!DOCTYPE html>
<html>
<head>
    <title>Controlador PWM Motores</title>
    <style>
        .motor-control {
            display: flex;
            gap: 20px;
            margin: 20px;
        }
        .slider {
            width: 200px;
        }
        .status {
            background: #f0f0f0;
            padding: 10px;
            margin: 10px;
            border-radius: 5px;
        }
    </style>
</head>
<body>
    <h1>Controlador PWM para Motores DC</h1>
    
    <div class="motor-control">
        <div>
            <h3>Motor A</h3>
            <label>Velocidade: <input type="range" id="speedA" min="0" max="255" value="0" class="slider"></label>
            <label>Direção: 
                <select id="dirA">
                    <option value="-1">Anti-horário</option>
                    <option value="0" selected>Parado</option>
                    <option value="1">Horário</option>
                </select>
            </label>
        </div>
        
        <div>
            <h3>Motor B</h3>
            <label>Velocidade: <input type="range" id="speedB" min="0" max="255" value="0" class="slider"></label>
            <label>Direção: 
                <select id="dirB">
                    <option value="-1">Anti-horário</option>
                    <option value="0" selected>Parado</option>
                    <option value="1">Horário</option>
                </select>
            </label>
        </div>
    </div>
    
    <div>
        <button onclick="startSystem()">START</button>
        <button onclick="stopSystem()">STOP</button>
        <button onclick="requestStatus()">STATUS</button>
    </div>
    
    <div id="status" class="status">
        Status: Desconectado
    </div>

    <script>
        // Implementação WebBluetooth para controle direto
        let bluetoothDevice;
        let characteristic;

        async function connectBluetooth() {
            try {
                bluetoothDevice = await navigator.bluetooth.requestDevice({
                    filters: [{name: 'HC-05'}],
                    optionalServices: ['0000ffe0-0000-1000-8000-00805f9b34fb']
                });
                
                const server = await bluetoothDevice.gatt.connect();
                const service = await server.getPrimaryService('0000ffe0-0000-1000-8000-00805f9b34fb');
                characteristic = await service.getCharacteristic('0000ffe1-0000-1000-8000-00805f9b34fb');
                
                document.getElementById('status').textContent = 'Status: Conectado';
            } catch(error) {
                console.error('Erro na conexão:', error);
            }
        }

        async function sendCommand(command) {
            if (characteristic) {
                const encoder = new TextEncoder();
                await characteristic.writeValue(encoder.encode(command + '\n'));
            }
        }

        document.getElementById('speedA').addEventListener('input', (e) => {
            sendCommand(`PWM_A:${e.target.value}`);
        });

        document.getElementById('speedB').addEventListener('input', (e) => {
            sendCommand(`PWM_B:${e.target.value}`);
        });

        document.getElementById('dirA').addEventListener('change', (e) => {
            sendCommand(`DIR_A:${e.target.value}`);
        });

        document.getElementById('dirB').addEventListener('change', (e) => {
            sendCommand(`DIR_B:${e.target.value}`);
        });

        function startSystem() {
            sendCommand('START');
        }

        function stopSystem() {
            sendCommand('STOP');
        }

        function requestStatus() {
            sendCommand('STATUS');
        }

        // Conectar automaticamente ao carregar
        window.addEventListener('load', connectBluetooth);
    </script>
</body>
</html>

🔬 Testes e Calibração

Procedimento de Teste

  1. Teste de Alimentação:

    void testPowerSupply() {
      Serial.println("=== TESTE DE ALIMENTAÇÃO ===");
      float voltage = readVoltage();
      Serial.print("Tensão medida: "); Serial.print(voltage); Serial.println("V");
      
      if(voltage < 11.0 || voltage > 13.0) {
        Serial.println("ERRO: Tensão fora da faixa!");
        return;
      }
      Serial.println("Alimentação OK");
    }
    
  2. Teste de Motores:

    void testMotorPerformance() {
      Serial.println("=== TESTE DE PERFORMANCE ===");
      
      for(int pwm = 50; pwm <= 255; pwm += 50) {
        setMotorTarget(&motorA, pwm, 1);
        updateMotor(&motorA);
        delay(2000);
        
        Serial.print("PWM: "); Serial.print(pwm);
        Serial.print(" - RPM: "); Serial.println(motorA.currentRPM);
      }
      
      stopAllMotors();
    }
    
  3. Calibração de RPM:

    void calibrateRPMSensor() {
      Serial.println("=== CALIBRAÇÃO RPM ===");
      Serial.println("Gire o motor manualmente 10 voltas...");
      
      motorA.rpmCount = 0;
      unsigned long startTime = millis();
      
      while(motorA.rpmCount < 10) {
        calculateMotorRPM(&motorA);
        delay(10);
      }
      
      unsigned long endTime = millis();
      float actualRPM = (10.0 * 60000.0) / (endTime - startTime);
      
      Serial.print("RPM calculado: "); Serial.println(actualRPM);
    }
    

📊 Melhorias e Expansões

Expansões Possíveis:

  1. Controle PID:

    class PIDController {
      private:
        float kp, ki, kd;
        float lastError = 0;
        float integral = 0;
        
      public:
        PIDController(float p, float i, float d) : kp(p), ki(i), kd(d) {}
        
        float calculate(float setpoint, float actual, float deltaTime) {
          float error = setpoint - actual;
          integral += error * deltaTime;
          float derivative = (error - lastError) / deltaTime;
          
          float output = kp * error + ki * integral + kd * derivative;
          lastError = error;
          
          return constrain(output, -255, 255);
        }
    };
    
  2. Comunicação WiFi:

    #include <WiFi.h>
    #include <WebServer.h>
    
    void setupWiFi() {
      WiFi.begin("SSID", "PASSWORD");
      while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.println("Conectando ao WiFi...");
      }
      Serial.println("WiFi conectado!");
    }
    
  3. Data Logging:

    void logData() {
      String logEntry = String(millis()) + "," +
                       String(motorA.currentRPM) + "," +
                       String(motorB.currentRPM) + "," +
                       String(readVoltage()) + "," +
                       String(readTemperature());
      
      // Salvar em SD card ou enviar via WiFi
      Serial.println(logEntry);
    }
    

✅ Conclusão

Este projeto demonstra um controlador PWM avançado para motores DC, combinando teoria sólida com implementação prática. O sistema oferece controle preciso, múltiplas funcionalidades e proteções robustas para aplicações profissionais.

Principais Aprendizados:

  • 🎛️ Controle PWM de alta precisão
  • 🔧 Integração de múltiplos sensores
  • 📱 Interfaces de usuário modernas
  • 🛡️ Sistemas de proteção robustos
  • 📊 Monitoramento em tempo real

⚡ Projeto desenvolvido pela equipe Fixtron Circuits com foco em educação técnica e aplicações industriais.