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
-
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"); }
-
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(); }
-
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:
-
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); } };
-
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!"); }
-
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.