arduinomicrocontrollerinterfacing

Arduino Interfacing with Magnetic Pickup


Currently I have a diesel engine with magnetic pickup attached to it. I want to use Arduino (Uno/Nano) to measure engine RPM.

Magnetic Pickup Description: A magnetic pickup is installed over a gear, (most commonly the flywheel inside a vehicle’s bell housing) and as the gear turns the pickup will create an electric pulse for each tooth on the gear. These pulses are then read by the instrument which interprets it to indicate the correct RPMs or speed.The signal from the magnetic speed Sensor, teeth per second(HZ), is directly proportional to engine speed.

Magnetic Pickup Image: MP - Self Powered

I've tried to rectify the signal using diode then limit the current using a resistor with .1Uf capacitor to filter the noise, then connected it to Optocopler 4N35 and the output from Opto to Arduino interrupt pin, by just observing Arduino interrupt ping is highly affected by surroundings.

Also I have tried to directly connect the magnetic pickup to "A0" pin and use analogue read and connect a led to pin 13 just to monitor the pulses from MP.

int sensorPin = A0;    
int ledPin = 13;      
int sensorValue = 0;  

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // read the value from the sensor:
  sensorValue = analogRead(sensorPin);
  digitalWrite(ledPin, HIGH);
  delay(sensorValue);
  digitalWrite(ledPin, LOW);
  Serial.println(sensorValue);
  Serial.println(" ");
}

Using analogueRead works with the LED as indicator for pulses generated by pickup. (Tested using small motor and small gear to protect Arduino).

Also I tried to use LM139 Comparator but the readings make no sense (ex: 60 RPM, 1500 RPM,2150 RPM, 7150 RPM).

LM139 Circuit

Code used with LM139:

// read RPM
volatile int rpmcount = 0;
//see http://arduino.cc/en/Reference/Volatile
int rpm = 0;
unsigned long lastmillis = 0;

void setup() {
  Serial.begin(9600);
  attachInterrupt(0, rpm_fan, RISING);
  //interrupt cero (0) is on pin two(2).
}

void loop() {
  if (millis() - lastmillis == 500) {
    /*Update every one second, this will be equal to reading frequency (Hz).*/
    detachInterrupt(0); //Disable interrupt when calculating
    rpm = rpmcount * 60;
    /* Convert frequency to RPM, note: this works for one interruption per full rotation. For two interrupts per full rotation use rpmcount * 30.*/
    Serial.print(rpm); // print the rpm value.
    Serial.println(" ");
    rpmcount = 0; // Restart the RPM counter
    lastmillis = millis(); // Update lastmillis
    attachInterrupt(0, rpm_fan, RISING); //enable interrupt
  }
}

void rpm_fan() {
  /* this code will be executed every time the interrupt 0 (pin2) gets low.*/
  rpmcount++;
}
// Elimelec Lopez - April 25th 2013

What is the best way or approach to interface a magnetic pickup with Arduino to display RPM?


Solution

  • Your use of analogRead is wrong. Besides, analogRead will not get you anywhere close to what you want to achieve.

    What you want from your pickup is a clear 0-5v digital signal. You can obtain that by playing with the input resistor on your opto-coupler. I'd do some measurements, and place a trimpot + resistors on the board do the actual value can be tweaked after the system is installed.

    Once you get the electrical signal as clean as you can get, you can the use an interrupt pin on the Arduino to keep count of the number of pulses.

    #define SENSOR_PIN  (2)    // using define instead of variable for constants save memory.
    #define LED_PIN     (13)
    
    #define READ_DELAY  (100)  // in milliseconds.
    
    // we'll get a reading every 100ms, so 8 bits are enough to keep
    // track of time.  You'd have to widen to unsigned int if you want 
    // READ_DELAY to exceed 255 ms.
    // 
    typedef delay_type unsigned char;
    
    typedef unsigned int counter_type;  // You may want to use 
                                        // unsigned long, if you 
                                        // experience overflows.
    
    volatile counter_type pulseCount = 0; // volatile is important here
    
    counter_type lastCount = 0;
    delay_type lastTime = 0;
    
    // pulse interrupt callback, keep short.
    void onSensorPulse()
    {
        ++pulseCount;
    
        // the following may already be too long.  Use for debugging only
        // digitalWrite() and digitalRead() are notoriously slow.
        // 
        // 
        // digitalWrite(LED_PIN, !digitalRead(LED_PIN));
        //
        // using fastest direct port access instead. (for ATMega)
        //
        if (pulseCount & 1)
            PORTB |= (1 << PB5);
        else
            PORTB &= ~(1 << PB5);
    }
    
    void setup() 
    {
        pinMode(SENSOR_PIN, INPUT);
        attachInterrupt(digitalPinToInterrupt(SENSOR_PIN), onSensorPulse, RISING);
    
        pinMode(ledPin, OUTPUT);
        Serial.begin(9600);
    }
    
    void loop() 
    {
        // control frequency of readings
        //
        delay_type now = (delay_type)millis();
        if (now - lastTime < READ_DELAY)
        {
            return;
        }
        lastTime = now;
    
        // get a reading.  must disable interrupts while doing so.
        // because pulseCount is multi-bytes.
        //
        noInterrupts();
        counter_type curCount = pulseCount;
        interrupts();
    
        // get the number of pulses since last reading.
        //
        counter_type delta = curCount - lastCount;
        lastCount = curCount;
    
        // to convert to RPMs, you will need to use this formula:
        // note the use of long (UL) to avoid overflows in the
        // computation.  60000 = miliseconds per minute.
        //
        // RPM = delta * 60000UL / (READ_DELAY * TEETH_COUNT);
    
        // send delta to client for now.
        //
        Serial.println(delta);
    }