Wednesday, January 26, 2011

Let's Add Sensors, Part 1

Now that we can act on the physical realm, let's add sensors to observe it.  There's all sorts of sensors out there, but there three basic interfaces to those sensors, digital, analog or via some sort of higher-level communications protocol (i2c, serial, parallel, et cetera).

I've got some of each of these, so we'll go through and try them each out one-by-one. Let's pick the digital inputs first.

To demonstrate this interface, I'm going to use a push-button switch.  These things are everywhere.  Most of mine were harvested from an old VCR.  This is the simplest sensor for use with your arduino, because, well, the output is either 1 or 0. (We will ignore debounce for now.)

Since you can read the input simply by reading in the pin, the easiest thing to do would be to put this logic into your loop() routine and do it repetitively.  Alternatively, you could set up an interrupt.  Interrupts are special creatures that allow a routine (commonly referred to as an Interrupt Service Routine, ISR for short) to get called when a pin's state changes.  By migrating your "on change" code into the ISR, you're doing less work on every loop.

I'm deciding to go the interrupt path, because it allows me to demonstrate more things at once, and well, it sounds like more fun.  My sketch will have a button that turns off our LED from the previous examples.  The button will trigger the toggleLed() ISR.  Also, for the sake of simplifying the electronics, the pin adjacent to the BUTTON_PIN (I'm calling it DRIVE_PIN) will be used to identify the button state and the button will be between the two pins.  When the button is pressed, the BUTTON_PIN will observe a low state (based on the state of the DRIVE_PIN) and vice-versa.  To ensure that the BUTTON_PIN state is high by default, we are using internal pull-up resistors in the chip.  The schematic for this set-up is below:



/*
   * Push Button Test
   *
   */
   
  #define LED_PIN 13         
  #define INTERRUPT_NUM 1         // This identifies the interrupt number for
                                  // our chipset:
                                  //   0 = digial pin 2
                                  //   1 = digital pin 3
                                
  #define BUTTON_PIN 3            // set up the button pin to match the interrupt
                                  // number.
  #define DRIVER_PIN 4            // set up the a pin drive the button

  volatile int led_state = HIGH;  // identify the current LED state
                                  // this is volatile because the toggleLed
                                  // routine which changes the value will be
                                  // called from an interrupt. 

  void toggleLed()
  {
    // read in the current state of the button
    // and enable the LED state based on the button state
    led_state=digitalRead(BUTTON_PIN);
    
    // write the new state out to the LED pin
    digitalWrite(LED_PIN, led_state);
  }


  void setup()
  {
    // set up the same LED as we have been
    pinMode(LED_PIN, OUTPUT);
    
    // add the driver pin
    pinMode(DRIVER_PIN, OUTPUT);
    // set the driver pin to LOW
    digitalWrite(DRIVER_PIN, LOW);
    
    // add the driver pin
    pinMode(BUTTON_PIN, INPUT);
    // enable the pull-up register
    digitalWrite(BUTTON_PIN, HIGH);
    
    // Identify the routine and conditions for our interrupt. An interrupt
    // can have only one routine attached, and that routine takes no inputs
    // and returns no output.  The specified routine will be called in 
    // the midst of the normal loop processing, so any variables that are
    // changed should probably be marked as volatile, if they are real-time
    // critical.  For our purposes, it's not necessary, but it may be 
    // significant if we have more robust processing of data changed in an 
    // interrupt routine.
    attachInterrupt(INTERRUPT_NUM, toggleLed, CHANGE); 
  }

  void loop()
  {
    // nothing to do here, since it's all taken care of in the interrupt
  }


Now, we can control the state of the LED manually by pressing a button.  This isn't earth shattering or particularly complex, but it does demonstrate a couple of principles (ISRs, Digital write, digital read, internal pull-up resistors.)  Another interesting note is that you don't need to actually read the interrupt pin in the ISR, it could be used to trigger something completely different, like reading different pins' values, or enabling some additional behavior, or even waking up the arduino (think back to the sleep mode we entered in the last post).

Looking forward, and applying what we've learned...

Eventually, I'm thinking that I'll use these buttons to detect when my little bot has bumped into something.  Because the arduino only has 2 interrupt pins, I'm going to need to be creative when doing this. I've got an assortment of ICs, so I think I'm going to "XOR" their states together to drive the ISR pin, so that I can have the ISR trigger when any of their states change. I will also connect them to digital pins, so that in the ISR, we can read all the inputs.  I plan to use the 74LS86N Quad 2-input XOR gate from my goody-bag.

No comments:

Post a Comment