domingo, 29 de setembro de 2024

Como transformar um torno em tacômetro e contador de voltas com Arduino e Sensor Hall

Quem trabalha com máquinas operatrizes ou oficinas em geral sabe como é útil ter uma leitura precisa da velocidade de rotação do equipamento. Recentemente, desenvolvi um projeto para instalar um sensor Hall e um display de 7 segmentos no meu torno mecânico usando um Arduino Nano.

A grande vantagem dessa implementação é que o circuito ganhou dupla função:
1. Modo F001 (Tacômetro / RPM): Mede as rotações por minuto em tempo real.
2. Modo F002 (Contador de Voltas): Conta o número total de voltas acumuladas, ideal para tarefas de precisão como enrolamento de fios, bobinas e transformadores.

Como Funciona o Circuito

O sensor Hall fica posicionado no eito/árvore do torno para detectar a passagem de um pequeno ímã. Cada passagem aciona uma interrupção digital no pino D2 do Arduino Nano, garantindo que nenhum pulso seja perdido, mesmo em altas velocidades.

O painel conta com um display de 7 segmentos de 4 dígitos (gerenciado pela biblioteca SevSeg) e dois botões de controle:
- Botão SET (pino A4): Alterna entre as funções F001 (RPM) e F002 (Contador de Voltas), exibindo a indicação da função escolhida no display por 2 segundos.
- Botão RESET (pino A5): Zera a contagem de voltas quando o sistema está no modo F002.

Aplicações Práticas

- Uso em Usinagem: Acompanhar a rotação exata ajuda a selecionar a velocidade de corte adequada para diferentes materiais e ferramentas.
- Uso como Rebobinadora: O modo contador facilita imensamente o enrolamento de bobinas, pequenos transformadores e motores de forma precisa sem precisar contar manualmente.

Código Completo para Arduino Nano

(NOVO) Código disponível para download em: https://github.com/aobatroz/rpmcounter-4digit-display

Abaixo está o código-fonte desenvolvido. Para compilá-lo, certifique-se de ter a biblioteca SevSeg instalada na sua IDE do Arduino:

#include <SevSeg.h>
SevSeg sevseg;

#define MAX_WAIT_FREQ 2000

const byte ledPin = 13;
const byte interruptPin = 2; // contador de pulso (Sensor Hall)
const byte setButtonPin = A4; // botao set
const byte resetButtonPin = A5; // botao reset
const byte digitPins[] = {A0, 3, 4, 5}; // pinos dos digitos do display
const byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12, 13}; // pinos dos segmentos

unsigned long interruptCount = 0; // Contador de interrupcoes
unsigned long rpmCount = 0;
double rps = 0;
unsigned long lastPulse = millis();
unsigned long interval = 0;
unsigned long refreshTime = millis();
unsigned int mode = 0;
unsigned int functionMode = 1; // 1 para F001 (RPM), 2 para F002 (Contador)
bool forceDisplayValue = false;

void interruptFunction()
{
    interval = millis() - lastPulse;
    lastPulse = millis();
    interruptCount++; // Incrementa o contador de interrupcoes
}

void displayFunctionMode()
{
    char buffer[5];
    snprintf(buffer, sizeof(buffer), "F00%d", functionMode); // Formata a string F00X
    sevseg.setChars(buffer); // Exibe no display
    for (unsigned long start = millis(); millis() - start < 2000;) { // Exibe por 2 segundos
        sevseg.refreshDisplay();
    }
    sevseg.blank(); // Apaga o display
    sevseg.refreshDisplay();
}

void setup()
{
    Serial.begin(9600);

    const byte numDigits = 4;
    bool resistorsOnSegments = 0;

    pinMode(interruptPin, INPUT_PULLUP);
    pinMode(setButtonPin, INPUT_PULLUP);
    pinMode(resetButtonPin, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(interruptPin), interruptFunction, RISING);

    sevseg.begin(COMMON_CATHODE, numDigits, digitPins, segmentPins, resistorsOnSegments);
    sevseg.setBrightness(10);

    displayFunctionMode();
}

void F001()
{
    // Calculando rps (rotacoes por segundo)
    rps = 0;
    if (interval > 0 && (millis() - lastPulse) < MAX_WAIT_FREQ) // ignora leituras muito lentas
    {
        rps = 1000.0 / interval;
    }

    if ((millis() > refreshTime))
    {
        Serial.println(rps);
        sevseg.setNumber(rps * 60); // Converte RPS para RPM
        refreshTime = millis() + 200;
    }
}

void F002()
{
    static unsigned long lastInterruptCount = -1;

    if (digitalRead(resetButtonPin) == LOW)
    {
        interruptCount = 0; // Reseta o contador
    }

    if ((lastInterruptCount != interruptCount) || forceDisplayValue)
    {
        lastInterruptCount = interruptCount;
        Serial.println(interruptCount);
        sevseg.setNumber(interruptCount);        
        forceDisplayValue = false;
    }
}

void loop()
{
    // Alterna o modo ao pressionar o botao SET
    if (digitalRead(setButtonPin) == LOW)
    {
        functionMode = (functionMode % 2) + 1; // Alterna entre 1 e 2
        displayFunctionMode(); // Exibe o modo por 2 segundos
        forceDisplayValue = true;
        delay(200); // Debounce
    }

    if (functionMode == 1)
    {
        F001();
    }
    else if (functionMode == 2)
    {
        F002();
    }

    sevseg.refreshDisplay();
}

Se você gostou da ideia, pretende aplicar em alguma máquina aí da sua oficina ou tem sugestões de melhorias no código, deixe seu comentário!