Posicionador linear de precisão que usa um motor de passo para mover uma plataforma. O LCD exibe posição actual em mm, destino, velocidade em RPM e modo de operação. Dois botões definem o destino. Um botão de homing encontra o zero absoluto. Base conceptual de sistemas CNC de eixo único.
| Tipo de transmissão | Fórmula | Exemplo concreto |
|---|---|---|
| Parafuso roscado | passos/mm = PROT ÷ passo_rosca(mm) | 200 p/rot ÷ 0.8mm/rot (M5) = 250 p/mm |
| Porca de chumbo (lead screw) | passos/mm = PROT ÷ lead(mm) | 200 ÷ 2mm lead = 100 p/mm |
| Correia dentada GT2 | passos/mm = PROT ÷ (dentes × 2mm) | 200 ÷ (20 × 2) = 5 p/mm |
| Correia GT3 | passos/mm = PROT ÷ (dentes × 3mm) | 200 ÷ (20 × 3) ≈ 3.33 p/mm |
PASSOS_MM para o teu tipo de transmissão antes de carregar o sketch.| Componente | Pino Arduino | Nota |
|---|---|---|
| LCD 20×4 (I2C) | A4 (SDA) / A5 (SCL) | Endereço 0x27 |
| Motor de passo (M1+M2) | Canal 1 da placa | AF_Stepper(200,1) |
| Botão + (aumentar alvo) | D2 (INPUT_PULLUP) | Incrementa 1mm por pressão |
| Botão − (diminuir alvo) | D3 (INPUT_PULLUP) | Decrementa 1mm por pressão |
| Botão GO (executar movimento) | D4 (INPUT_PULLUP) | Inicia movimento para o alvo |
| Botão HOME (homing) | D5 (INPUT_PULLUP) | Volta ao zero e redefine posição |
| Fim de curso (sensor zero) | D6 (INPUT_PULLUP) | LOW quando plataforma em posição 0 |
Liga o motor ao canal 1. Carrega um sketch de teste: stepper.step(200, FORWARD, SINGLE). Confirma que roda. Liga o LCD e exibe 'Motor OK'.
Liga o botão de fim de curso ao D6. Implementa a função homing(): recua até LOW em D6, define posAtual=0. Testa isoladamente.
Implementa os botões + e − para alterar alvomm. Ao premir GO, calcula passos necessários e executa irPara(). Exibe posição e destino no LCD.
Adiciona rampa de aceleração no irPara(). Implementa barra gráfica de progresso do movimento na linha 3.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <AFMotor.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
AF_Stepper stepper(200, 1);
// ── Parâmetros mecânicos ─────────────────────────────────────
#define PASSOS_MM 250 // Parafuso M5 (0.8mm/rot × 200 p/rot)
#define POS_MAX_MM 80 // Curso máximo em mm
#define RPM_NORMAL 60
#define RPM_MIN 15 // RPM mínimos para a rampa
// ── Botões ────────────────────────────────────────────────────
#define BTN_MAIS 2
#define BTN_MENOS 3
#define BTN_GO 4
#define BTN_HOME 5
#define FIM_CURSO 6
// ── Estado ────────────────────────────────────────────────────
float posMM = 0; // posição actual em mm
float alvoMM = 0; // destino em mm
long posP = 0; // posição em passos
unsigned long tBtn = 0;
// ── Converter mm → passos ─────────────────────────────────────
long mmToP(float mm) { return (long)(mm * PASSOS_MM); }
float pToMM(long p) { return (float)p / PASSOS_MM; }
// ── Dashboard LCD ─────────────────────────────────────────────
void dashboard(const char* estado, int rpm) {
lcd.setCursor(0,0); lcd.print("Posicionador CNC ");
lcd.setCursor(0,1);
lcd.print("Pos:"); lcd.print(posMM,1); lcd.print("mm Alv:");
lcd.print(alvoMM,0); lcd.print("mm ");
lcd.setCursor(0,2);
lcd.print("Vel:"); lcd.print(rpm); lcd.print("RPM ");
lcd.print(estado); lcd.print(" ");
}
// ── Barra de progresso do movimento ──────────────────────────
void barraProgresso(long atual, long total) {
int b = (total > 0) ? (int)(abs(atual) * 20 / total) : 20;
b = constrain(b, 0, 20);
lcd.setCursor(0,3); lcd.print("[");
for(int i=0;i<18;i++) i<b-1 ? lcd.write(0xFF) : (i==b-1 ? lcd.write('+') : lcd.write(' '));
lcd.print("]");
}
// ── Homing ────────────────────────────────────────────────────
void homing() {
lcd.setCursor(0,2); lcd.print("HOMING... ");
stepper.setSpeed(RPM_MIN);
while(digitalRead(FIM_CURSO) == HIGH)
stepper.step(1, BACKWARD, SINGLE);
posP = 0; posMM = 0; alvoMM = 0;
stepper.release();
stepper.setSpeed(RPM_NORMAL);
lcd.setCursor(0,2); lcd.print("HOME OK pos=0 ");
delay(800);
}
// ── Ir para posição com rampa ─────────────────────────────────
void irPara(float mm) {
mm = constrain(mm, 0, POS_MAX_MM);
long alvoP = mmToP(mm);
long delta = alvoP - posP;
if(delta == 0) return;
uint8_t dir = (delta > 0) ? FORWARD : BACKWARD;
long absD = abs(delta);
int ramp = min(absD/3, (long)60);
for(long i=0; i<absD; i++) {
int rpm;
if (i < ramp) rpm = map(i, 0, ramp, RPM_MIN, RPM_NORMAL);
else if(i > absD-ramp) rpm = map(absD-i, 0, ramp, RPM_MIN, RPM_NORMAL);
else rpm = RPM_NORMAL;
stepper.setSpeed(rpm);
stepper.step(1, dir, SINGLE);
posP += (dir==FORWARD) ? 1 : -1;
posMM = pToMM(posP);
if(i % 20 == 0) { // actualiza LCD a cada 20 passos
dashboard("EM CURSO", rpm);
barraProgresso(i, absD);
}
}
alvoMM = mm;
stepper.release();
dashboard("NO ALVO ", 0);
barraProgresso(1, 1); // barra cheia
}
void setup() {
lcd.init(); lcd.backlight();
stepper.setSpeed(RPM_NORMAL);
for(int p : {BTN_MAIS,BTN_MENOS,BTN_GO,BTN_HOME,FIM_CURSO})
pinMode(p, INPUT_PULLUP);
homing();
dashboard("PRONTO ", 0);
}
void loop() {
if(millis()-tBtn < 200) return; // debounce global
if(digitalRead(BTN_MAIS)==LOW) { alvoMM=constrain(alvoMM+1,0,POS_MAX_MM); tBtn=millis(); }
if(digitalRead(BTN_MENOS)==LOW) { alvoMM=constrain(alvoMM-1,0,POS_MAX_MM); tBtn=millis(); }
if(digitalRead(BTN_GO)==LOW) { irPara(alvoMM); tBtn=millis(); }
if(digitalRead(BTN_HOME)==LOW) { homing(); tBtn=millis(); }
dashboard("PRONTO ", 0);
}