El otro día en el directo de Twitch, estuve probando un sensor Hall y aprendimos como se usaba. Básicamente lo usé para hacerme un tacómetro DIY / cuenta revoluciones, cómo quieras llamarlo. Y vamos a ver los pasos que seguí para ir mejorando el código de conteo.
Funcionamiento y componentes
El circuito es muy sencillo, únicamente conectamos el sensor Hall SH41 a una entrada digital cualquiera, D5 por ejemplo. Además necesitamos la resistencia de pull-up para el SH41 de la salida al voltaje de alimentación.

El funcionamiento del sensor y del circuito se puede resumir en que el sensor Hall va a cambiar su salida de alto-bajo y viceversa cuando detecte un cambio de polaridad en el campo magnético que detecta. Es decir, si ponemos un imán delante suyo, y lo giramos de manera que alternemos el polo norte y sur del imán, el sensor nos irá cambiando la salida de alto a bajo cuando detecte el giro.
Si conectamos el imán al eje de un motor, podremos contar las revoluciones por minuto del motor, contando cuantas veces hay flancos en el sensor Hall, y eso es lo que vamos a hacer hoy.
Sensores Hall | ![]() |
Arduino Nano o equivalente | ![]() |
Genéricos (Cables, resistencias…) |
Código 1: Entendiendo el sensor Hall
El primer código es muy intuitivo, configuramos el pin digital y simplemente comprobamos su valor cada 500ms. Este código nos sirve para ver que todo está bien conectado y que el sensor Hall funciona. Para probarlo, lo podemos flashear en nuestro Arduino, y girar un imán cerca del sensor Hall. Tendremos que ver que la salida va cambiando entre HIGH y LOW en función del polo del imán.
#include <Arduino.h> #define HALL_PIN 5 void setup(){ // Activamos el serial para poder sacar datos por pantalla Serial.begin(9600); pinMode(HALL_PIN, INPUT); } void loop(){ // Comprobamos el valor del pin y lo mostramos para depurar if(digitalRead(HALL_PIN) == HIGH){ Serial.println("HIGH"); }else{ Serial.println("LOW"); } delay(500) }
Código 2: Mejoramos con interrupciones
El principal problema del código anterior es que es muy lento, aunque redujéramos el delay, no podríamos hacer mucho más de procesamiento antes de perder pulsos del sensor hall. Imagina que quieres envíar datos por Wifi a tu PC, o pintar las RPM en la pantalla, en esos casos, no le daría tiempo al código a hacer todo y además tener en cuenta los cambios en el pin de entrada.
Para ello están nuestras amigas las interrupciones, recordad que las interrupciones tienen que ser lo más cortas posibles, tienen que hacer el menor número de operaciones posibles. Así que lo más fácil sería, por ejemplo, tener un contador de pulsos, y que:
- Cada vez que el pin D5 detectase un flanco de subida, incrementase el valor del contador.
- Cada 10 segundos, mirase cuantos pulsos se han detectado
- Multiplicamos el valor por 6 y tenemos las vueltas por minuto!
Además, ese código puede hacer otras cosas en paralelo, sin que se pierdan pulsos! Pero hay que tener cuidado, ya que hay que conectar el sensor Hall a un pin que esté conectado a una interrupción! En el caso de Arduino nano son los pines 2 y 3.
Así que tenemos que cambiar el pin de entrada al pin D2.
#include <Arduino.h> // Pin con interrupcion! #define HALL_PIN 2 uint32_t pulses; void increaseCount(){ pulses++; } void setup(){ // Activamos el serial para poder sacar datos por pantalla Serial.begin(9600); pinMode(HALL_PIN, INPUT); // Detectamos solo las subidas, ya que si detectamos ambas // tendríamos dos pulsos por vuelta. attachInterrupt(HALL_PIN, increaseCount, RISING); } void loop(){ Serial.println(pulses*6); delay(10000) }
Lo mejor de este código es que el bucle principal queda más limpio que antes, ya únicamente cogemos el valor y lo usamos sin hacer nada raro.
Código 3: La Crème de la crème usando registros del Timer1
La siguiente y última opción es usar los registros del temporizador, y este es, a mi modo de ver, la mejor opción. Y es que de esta manera, no se ejecuta nada en código para contar pulsos! Todo lo hace el hardware en riguroso proceso paralelo.
Pero, y los timers? Porque los sacamos ahora? Me alegra que me hagas esa pregunta. Un temporizador/contador de un ATMega, cómo puede ser de otros micros, no es más que un número que se va incrementando cuando lo dice un reloj, y que avisa al procesador cuando llega a un valor que tú le has dicho.
Es decir que tenemos un número que sube cuando detecta un flanco? Justo lo que estábamos haciendo hasta ahora! Podemos decirle al contador 1, que su reloj sea la señal del sensor Hall, y cada X tiempo miramos el valor del contador!
Para ello tenemos que configurar el reloj del Timer1, para ello vamos al datasheet del ATMega y buscar la información del Timer1. Podemos ver que el registro TCCR1B tiene este campo:

Podemos ver que que si el CS12-CS10 vale 0b110 o 0b111, el reloj lo toma de la entrada T1, así que solo tenemos que ver donde está T1, que podréis ver en cualquier pinout de Arduino, que en el nano es la entrada D5 (qué casualidad…). Lo siguiente es ver que otra configuración necesitamos del Timer1.
Os recomiendo ojear un poco el datasheet y ver que modos de operación tiene, pero ya os adelanto, que en este caso queremos un contador hacía arriba y nada más, no necesitamos nada más exótico, así que el resto de configuración vamos a dejarlo a 0.
Por último, el valor del contador en cada instante se puede leer mediante los registros TCNT1L y TCNT1H, pero que en Arduino están juntos en el TCNT1
#include <Arduino.h> #define HALL_PIN 5 uint32_t pulses; void configureTimer1ExtClock(){ // Normal configuration, no Output, counting up to 0xFFFF TCCR1A = 0; // External Clock on T1 pin TCCR1B = 1 << CS12 | 1 << CS11 | 0 << CS10; } void setup(){ // Activamos el serial para poder sacar datos por pantalla Serial.begin(9600); pinMode(HALL_PIN, INPUT); configureTimer1ExtClock(); } void loop(){ Serial.println(TCNT1*6); delay(10000) }