Attaching more than one sensors that work with interrupts and instantiating them

Hi,

I am trying to build a library where i would like the user to be able to attach some sensors (ultra sound in this case) to the interrupt pins of arduino and to be able to get data from them without caring (so much) how much it takes for them to return the data.

So, in my case the pulseIn function is unacceptable since it halts the execution of the program.
I found the following sketch by Steve Garratt that works as intended with one ultra sound sensor.

I incorporated this into my library and also works as intended. However, i had (?) to use many static variables due to the ISR and therefore if i would like to add a second sensor, i’m almost sure that it wouldn’t work properly.

My question is, what’s the best way to do what i want?
One solution would be to have more static variables (i know, it sounds very bad), a separate set for each sensor (i will have max 3, so it’s not that many) and i update only the ones i need every time.
However, that sounds to me like a really bad practice.

Any better ideas?

The code follows…

Steve Garratt’s initial example, that works perfectly fine with one ultra sound sensor:

#include <TimerOne.h>                                 // Header file for TimerOne library

#define trigPin 39                                    // Pin 12 trigger output
#define echoPin 2                                     // Pin 2 Echo input
#define onBoardLED 13                                 // Pin 13 onboard LED
#define echo_int 0                                    // Interrupt id for echo pulse

#define TIMER_US 50                                   // 50 uS timer duration 
#define TICK_COUNTS 4000                              // 200 mS worth of timer ticks

volatile long echo_start = 0;                         // Records start of echo pulse
volatile long echo_end = 0;                           // Records end of echo pulse
volatile long echo_duration = 0;                      // Duration - difference between end and start
volatile int trigger_time_count = 0;                  // Count down counter to trigger pulse time
volatile long range_flasher_counter = 0;              // Count down counter for flashing distance LED

// ----------------------------------
// setup() routine called first.
// A one time routine executed at power up or reset time.
// Used to initialise hardware.
// ----------------------------------
void setup()
{
  pinMode(trigPin, OUTPUT);                           // Trigger pin set to output
  pinMode(echoPin, INPUT);                            // Echo pin set to input
  pinMode(onBoardLED, OUTPUT);                        // Onboard LED pin set to output
  Timer1.initialize(TIMER_US);                        // Initialise timer 1
  Timer1.attachInterrupt( timerIsr );                 // Attach interrupt to the timer service routine
  attachInterrupt(echo_int, echo_interrupt, CHANGE);  // Attach interrupt to the sensor echo input
  Serial.begin (9600);                                // Initialise the serial monitor output
}

// ----------------------------------
// loop() Runs continuously in a loop.
// This is the background routine where most of the processing usualy takes place.
// Non time critical tasks should be run from here.
// ----------------------------------
void loop()
{
  Serial.println(echo_duration / 58);               // Print the distance in centimeters
  delay(100);                                       // every 100 mS
}

// --------------------------
// timerIsr() 50uS second interrupt ISR()
// Called every time the hardware timer 1 times out.
// --------------------------
void timerIsr()
{
  trigger_pulse();                                 // Schedule the trigger pulses
  distance_flasher();                              // Flash the onboard LED distance indicator
}

// --------------------------
// trigger_pulse() called every 50 uS to schedule trigger pulses.
// Generates a pulse one timer tick long.
// Minimum trigger pulse width for the HC-SR04 is 10 us. This system
// delivers a 50 uS pulse.
// --------------------------
void trigger_pulse()
{
  static volatile int state = 0;                 // State machine variable

  if (!(--trigger_time_count))                   // Count to 200mS
  { // Time out - Initiate trigger pulse
    trigger_time_count = TICK_COUNTS;           // Reload
    state = 1;                                  // Changing to state 1 initiates a pulse
  }

  switch (state)                                 // State machine handles delivery of trigger pulse
  {
    case 0:                                      // Normal state does nothing
      break;

    case 1:                                      // Initiate pulse
      digitalWrite(trigPin, HIGH);              // Set the trigger output high
      state = 2;                                // and set state to 2
      break;

    case 2:                                      // Complete the pulse
    default:
      digitalWrite(trigPin, LOW);               // Set the trigger output low
      state = 0;                                // and return state to normal 0
      break;
  }
}

// --------------------------
// echo_interrupt() External interrupt from HC-SR04 echo signal.
// Called every time the echo signal changes state.
//
// Note: this routine does not handle the case where the timer
//       counter overflows which will result in the occassional error.
// --------------------------
void echo_interrupt()
{
  switch (digitalRead(echoPin))                     // Test to see if the signal is high or low
  {
    case HIGH:                                      // High so must be the start of the echo pulse
      echo_end = 0;                                 // Clear the end time
      echo_start = micros();                        // Save the start time
      break;

    case LOW:                                       // Low so must be the end of hte echo pulse
      echo_end = micros();                          // Save the end time
      echo_duration = echo_end - echo_start;        // Calculate the pulse duration
      break;
  }
}

// --------------------------
// distance_flasher() Called from the timer 1 timerIsr service routine.
// Flashes the onboard LED at a rate inversely proportional
// to distance. The closer it gets the higher the frequency.
// --------------------------
void distance_flasher()
{
  if (--range_flasher_counter <= 0)                // Decrement and test the flash timer
  { // Flash timer time out
    if (echo_duration < 25000)                    // If the echo duration is within limits
    {
      range_flasher_counter = echo_duration * 2;  // Reload the timer with the current echo duration
    }
    else
    {
      range_flasher_counter = 25000;              // If out of range use a default
    }

    digitalWrite( onBoardLED, digitalRead( onBoardLED ) ^ 1 );   // Toggle the onboard LED
  }
}

My sketch, that the user will upload in the arduino (works for one sensor). The user instantiates a sensor, initializes it (by “attaching” it to the proper pins) and then gets the distance with getDistance().

#include <Smartcar_sensors.h>
#include <TimerOne_smartcar.h>

Sonar front;

const int triggerPin = 39;
const int echoPin = 2; //usually only pins 2, 18 are available. 19 available if odometer is not connected or active

void setup() {
  front.attach(triggerPin, echoPin);
  Serial.begin(9600);
}

void loop() {
  Serial.println(front.getDistance());
  delay(50);
  
}

my Smartcar_sensors.h header file:

/*
* Smartcar_sensors.h - A simple library for controlling various sensors
* (usually) placed on the smartcar.
* Version: 0.1
* Author:
*/
#ifndef Smartcar_sensors_h
#define Smartcar_sensors_h

#include "Arduino.h"
#include <TimerOne_smartcar.h>

/* Based on the code by Steve Garratt, found at: http://homediyelectronics.com/projects/arduino/arduinoprogramminghcsr04withinterrupts/ */
class Sonar
{
 public:
 Sonar();
 void attach(int triggerPin, int echoPin);
 void detach();
 int getDistance();

};



#endif

and my source file Sonar.cpp, basically turning Steve Garratt’s code into a library. (next post)

(continue from previous post)
and my source file Sonar.cpp, basically turning Steve Garratt’s code into a library:

/*
* Sonar.cpp - Handles the ultra sound sensors.
* (usually) placed on the smartcar.
* Version: 0.1
* Based on the code by Steve Garratt, found at: http://homediyelectronics.com/projects/arduino/arduinoprogramminghcsr04withinterrupts/
*/

#include "Smartcar_sensors.h"


static const int TIMER_US = 50;
static const int TICK_COUNTS = 4000;

volatile long echo_start;
volatile long echo_end;
volatile long echo_duration;
volatile int trigger_time_count; // Count down counter to trigger pulse time
int _triggerPin, _echoPin;

void trigger_pulse();
void echo_interrupt();

Sonar::Sonar(){
 echo_start = 0;
 echo_end = 0;
 echo_duration = 0;
 trigger_time_count = 0;
}

void Sonar::attach(int triggerPin, int echoPin){
 int _echoInterruptPin;
 switch(echoPin){ //mapping the digital pins to arduino mega interrupts
 case 2:
 _echoInterruptPin = 0;
 break;
 case 18:
 _echoInterruptPin = 5;
 break;
 case 19:
 _echoInterruptPin = 4;
 break; 
 }
 _triggerPin = triggerPin;
 _echoPin = echoPin;
 pinMode(triggerPin, OUTPUT);
 pinMode(echoPin, INPUT);
 Timer1.initialize(TIMER_US);
 Timer1.attachInterrupt(trigger_pulse); //Attach interrupt to the timer service routine
 attachInterrupt(_echoInterruptPin, echo_interrupt, CHANGE);
}

void Sonar::detach(){
 detachInterrupt(_triggerPin);
 Timer1.detachInterrupt();
 echo_duration = -1; //to signal an error if fetched later
}

int Sonar::getDistance(){

 if (echo_duration <= 0) return -1; // error signal -1: detached sonar
 int measuredDistance = echo_duration/58;
 if (measuredDistance<5 || measuredDistance>150) return 0; //error signal 0: value out of range
 return measuredDistance;
}

/* trigger_pulse(): called every 50 uS to schedule trigger pulses. Generates a pulse one timer tick long. Minimum trigger pulse width for the HC-SR04 is 10 us. This system delivers a 50 uS pulse. */

void trigger_pulse()
{
  static volatile int state = 0;                 // State machine variable

  if (!(--trigger_time_count))                   // Count to 200mS
  { // Time out - Initiate trigger pulse
    trigger_time_count = TICK_COUNTS;           // Reload
    state = 1;                                  // Changing to state 1 initiates a pulse
  }

  switch (state)                                 // State machine handles delivery of trigger pulse
  {
    case 0:                                      // Normal state does nothing
      break;

    case 1:                                      // Initiate pulse
      digitalWrite(_triggerPin, HIGH);              // Set the trigger output high
      state = 2;                                // and set state to 2
      break;

    case 2:                                      // Complete the pulse
    default:
      digitalWrite(_triggerPin, LOW);               // Set the trigger output low
      state = 0;                                // and return state to normal 0
      break;
  }
}

/* echo_interrupt(): External interrupt from HC-SR04 echo signal. Called every time the echo signal changes state. Note: this routine does not handle the case where the timer counter overflows which will result in the occassional error. */

void echo_interrupt(){
  switch (digitalRead(_echoPin))                     // Test to see if the signal is high or low
  {
    case HIGH:                                      // High so must be the start of the echo pulse
      echo_end = 0;                                 // Clear the end time
      echo_start = micros();                        // Save the start time
      break;

    case LOW:                                       // Low so must be the end of the echo pulse
      echo_end = micros();                          // Save the end time
      echo_duration = echo_end - echo_start;        // Calculate the pulse duration
      break;
  }
}

I didn't look at your code. There is a NewPing library that uses interrupts to read a ping sensor. It doesn't care which pins the sensors are attached to (though it might matter which board the code is running on).

The fact that you need a boatload of static variables means that you are trying to use a class in a way it is not designed to be used. You can use .h and .cpp files to collect functions, without the need to create a class. A class is useful when the instances of the class need to be independent. Static methods and static fields destroy that independence.

PaulS: I didn't look at your code. There is a NewPing library that uses interrupts to read a ping sensor. It doesn't care which pins the sensors are attached to (though it might matter which board the code is running on).

wow, i checked out that NewPing library fast and it seems to do exactly what I need! I'll try it out! Thanks! :)