High Speed Tachometer Triggered by External Source

Howdy Arduino Gurus,

Project Summary
Using an Arduino Uno, my code should poll pin 13's state while waiting for an external button to deliver 5v which then initiates a loop that constantly polls pin 2's state at 40hz. Each time the state changes at Oin 2, a counter is increased.

**Problems needing to be solved in order of importance. **

  1. When I press the external button, 5v is sent to the Arduino pin 13. This initiates the loop BUT as long as the button is pressed the voltage is constant causing the serial monitor to be populated at an insanely high rate instead of at 40hz. How can I make the button press start the loop without being affected by the length of the button press?
  2. Is there any way I can improve the synchronicity of the printing? It is SLIGHTLY slower than 40hz and will be off by a couple of seconds (4ish) after an hour of recording.
  3. I am currently solving bouncing issues after the fact in my spreadsheet as Delay functions will likely cause my polling to get off sync with my spectrometer.

**More details if needed. **
I am a scientist interested in neurotransmission during physical activity. I have created a tachometer (mouse running wheel, schematic below) with a reed switch (COM-10601 ROHS, Sparkfun) connected to pin 2 (internal pullup) and ground pin. Each time a magnet passes by the switch, the count should rise by 1 (in actuality, the reed switch bounces and counts multiple times). I am simultaneously running another machine (spectrometer) that measures the levels of a neurotransmitter at 40 HZ (25 milliseconds). The goal is to correlate the speed of the wheel with the level of neurotransmitter so I will combine the data by fixing time in column A, the level of the neurotransmitter in column B, and the serial monitor data from the reed switch counting in column C (example attached).

Here is my code:

volatile long frame = -1;
volatile int rotations;
int trigger = 0;    
int triggerPin = 13;                                              //the pin that the external trigger is attached to
int wheelPin = 2;                                                 //the pin that the Reed Switch is attached to
int wheelPin_irq = 0;                                             //IRQ that matches to pin 2
const unsigned long eventInterval = 25;                           //Hopeful sampling rate (40HZ)
volatile unsigned long previousTime = 0;


void setup() {

  Serial.begin(115200);
 
  pinMode(triggerPin, INPUT_PULLUP);
  pinMode(wheelPin, INPUT_PULLUP);
  attachInterrupt(wheelPin_irq, IRQcounter, RISING);
 
}

 
void IRQcounter() {

  rotations++;

}

 
void loop() {


  
  trigger = digitalRead(triggerPin);  
    if (trigger == HIGH) {
      frame = 0;      
    }

    while ( frame>=0 ) {
      unsigned long currentTime = millis();     
      if ( currentTime - previousTime >= eventInterval) {
        Serial.print(currentTime);
        Serial.print("\t");
        Serial.println(rotations);
        previousTime += eventInterval;
        frame = frame +1;
       
  }
  }
}

Thank you for any and all help. Those that help us rookies solve these problems are absolute life savers!!

Ayland

Uhhh... pin 13 is also the on-board LED, so you might try a different pin. Also, I see no ground connection to your external trigger, so how does the Arduino know when there is no external 5 volts?
Paul

You are new to Arduino and programming them.
Well done using code tags for posting the code.

Scrap that Fritzing, or hang it on the wall. Post a real drawing. That Fritzing ought to be stine dead, no power to the UNO.

You use pin 0, D0, for interrupts. D0 and D1 are used for uploading code and by the serial monitor communication. Use another pin.

Using INPUT_PULLUP is good. Then the device, button, or what, needs to be connected to GND. The signal from digitalRead gives a 0 when activated.

Think again what happends when the button is pressed! Loop is fast a spinns lots of times.

It looks like you want a change from 0 to 5V on pin 13 to start the test. Your schematic suggests it has to remain pressed to power/trigger the spectrometer. What stops the test? Is the button a latching type (press-on, press-off)?

'frame' does not look like it gets reported out; why track/increment it?

Railroader:
You use pin 0, D0, for interrupts. D0 and D1 are used for uploading code and by the serial monitor communication. Use another pin.

He's using pin 2. '0' is the interrupt number.

Thanks all for the replies so far!

Paul_KD7HB - I will move in the input from 13 to 12 (like in my fritz). I will also add in a connection to ground in that wire. I assume I should put in a resister too?

Railroader - You made me laugh out loud. I thought my fritzing was pretty. I can upload a circuit diagram, but I think the point is made already for this post. Blackfin is right, I am using pin 2.

Blackfin - Once the button is pressed once, the test should run forever. I manually stop the test in Arduino by reuploading the code and by stopping recording in my Spectrometer program. This really isn't a problem, so I did not waste time making code. I am incredibly new (this is my first project) so I am trying to go simple haha. I am not sure about the frame... thought it would be needed - I will try to remove it!

ayland:
Thanks all for the replies so far! No problem. We work for free.

Paul_KD7HB - I will move in the input from 13 to 12 (like in my fritz). I will also add in a connection to ground in that wire. I assume I should put in a resister too? Not needed when using INPUT_PULLUP.

Railroader - You made me laugh out loud. I thought my fritzing was pretty. I can upload a circuit diagram, but I think the point is made already for this post.

You think.... Every member asking questions is thinking that they are thinking and the mistakes are usual. Such an easy thing as how, from where, power is supplied is a usual reson for trouble, and hided behind a beautiful sails picture. What I write to You I hope more members will read. Engineering calls for drawings and is very often needed.

I manually stop the test in Arduino by reuploading the code
Press the reset button instead! The number of code loadings to the controller is generous but not unlimited.

Give this a try. See if you can use any of it:

//defines
#define DEADBAND_TIME   10000ul                 //uS    10mS    RPM sensor dead-band time
#define TRIG_READ_TIME  10ul                    //mS    10mS    time between trigger input reads

//switch return values
enum enSwitchStates
{
    TRIG_NONE=0,                                //no change from last read
    TRIG_WENT_LO,                               //switch was high, went low
    TRIG_WENT_HI                                //switch was low, went high
};

uint32_t frame;
volatile uint32_t rotations;
uint8_t trigger = 0;
uint8_t triggerPin = 12;                        //the pin that the external trigger is attached to
uint8_t wheelPin = 2;                           //the pin that the Reed Switch is attached to
uint8_t testPin = 7;
const unsigned long eventInterval = 25000ul;    //Hopeful sampling rate (40HZ)

enum enStates
{
    IDLE = 0,
    ACTIVE
};

uint8_t
    lastTrigger;

void setup() 
{
    Serial.begin(115200);
    
    pinMode(triggerPin, INPUT_PULLUP);
    pinMode(wheelPin, INPUT_PULLUP);    
    pinMode( testPin, OUTPUT );
 
}//setup
 
void IRQcounter() 
{
    static uint32_t
        timeLast=0;
    uint32_t
        timeNow = micros();

    //only allow rotation increment if deadband-time since last pulse has expired
    if( (timeNow - timeLast) >= DEADBAND_TIME )
    {
        rotations++;
        timeLast = timeNow;

    }//if

}//IRQcounter

uint8_t ReadTrigger( void )
{
    static uint32_t
        timeReadTrig;
    uint32_t
        timeNow = millis();

    //read the trigger pin every TRIG_READ_TIME mS
    if( (timeNow - timeReadTrig) >= TRIG_READ_TIME )
    {
        //look for a change in state of the trigger pin    
        timeReadTrig = timeNow;
        uint8_t nowTrigger = digitalRead( triggerPin );
        if( nowTrigger != lastTrigger )
        {
            lastTrigger = nowTrigger;
            return( (nowTrigger == HIGH) ? TRIG_WENT_HI : TRIG_WENT_LO );
        
        }//if

    }//if
    
    return TRIG_NONE;
    
}//ReadTrigger
void loop() 
{  
    static uint8_t
        stateFrame = IDLE;
    static uint32_t
        timeFrame;
    uint32_t
        timeNow = micros();
        
    switch( stateFrame )
    {
        case    IDLE:
            if( ReadTrigger() == TRIG_WENT_HI )
            {
                frame = 0;
                rotations = 0;
                timeFrame = timeNow;
                attachInterrupt( digitalPinToInterrupt( wheelPin), IRQcounter, RISING );
                stateFrame = ACTIVE;
                
            }//if
            
        break;

        case    ACTIVE:
            if( (timeNow - timeFrame) >= eventInterval )
            {
                digitalWrite( testPin, digitalRead(testPin) ^ HIGH );
                timeFrame = timeNow;
                Serial.print( timeFrame/1000ul );
                Serial.print("\t");
                noInterrupts();
                uint32_t tempRot = rotations;
                interrupts();
                Serial.println( tempRot );
                
                frame++;
                
            }//if
                
        break;
        
    }//switch
    
}//loop

Railroader: I hear you - this is importnat. I will work a schematic up. And that is good to know!

Blackfin: Woah, this is awesome! Thanks! I plugged copied in the code - 1. The timing seems to work great! 2. The code starts automatically without pressing the external trigger button (I detached the wire to make sure something screwy wasn't going on, and the code still ran without pressing it).

Thanks! Then we can likely rule out some usual errors.
Did You take a close lock at, and test, the reply in #7 by @Blackfin?

ayland:
Railroader: I hear you - this is importnat. I will work a schematic up. And that is good to know!

Blackfin: Woah, this is awesome! Thanks! I plugged copied in the code - 1. The timing seems to work great! 2. The code starts automatically without pressing the external trigger button (I detached the wire to make sure something screwy wasn't going on, and the code still ran without pressing it).

If you change setup() to:

void setup() 
{
    Serial.begin(115200);
    
    pinMode(triggerPin, INPUT_PULLUP);
    lastTrigger = digitalRead( triggerPin );
    pinMode(wheelPin, INPUT_PULLUP);    
    pinMode( testPin, OUTPUT );
 
}//setup

(i.e. add an initialization of lastTrigger by reading triggerPin) does that make a difference?

Can you verify your trigger pin switch wiring? You press the button to apply 5V to the pin; how is the pin controlled when the button isn't pressed? You initialize the pin mode as INPUT_PULLUP so if you have a pull-down resistor present you could have indeterminant voltage present on the pin due to a voltage-divider.

It'd be better if you could change the button wiring so that when the button is not closed, the switch is open and the pin idles "high" thanks to the INPUT_PULLUP mode; the other side of the switch is connected to GND so when the button is pressed the pin is grounded and you look for the high-to-low transition.

Pls verify: the spectrometer is triggered by the button press giving 5V to a "TRIG" input to the spectrometer?

@Blackfin
Thanks!
It's getting late here....... No focus left.

Sorry for the late reply, I had to switch to a different task for a bit.

~~When I plug in your new startup code, Blackfin, pressing the external trigger no longer starts the Arduino code. Of note, if I connect the voltage from the external button directly to pin 13, the LED turns on whenever I press the button (and stays on if held). Pressing this button no matter what the code is will always turn on my spectrometer as the button is essentially part of the spectrometer. ~~ E*dited after post as this comment was incorrect due to an error on my end. *

I have a long insulated wire coming straight from my external trigger that inserts directly into the Arduino pin #13 (or 12.. as I tried both in accordance with Paul_KD7HB's advice).

I will try the additional connection to GND ASAP.

DANG IT nevermind. I made a dumb mistake. IT WORKS!!!!

Seriously. Thank you for your help. I wish I could give more than Karma... but... maybe that is the most important thing in life?

Great! Thanks for telling!

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.