Eight green and eight red LEDs will also be connected to the Due as outputs. The immediate goal is to have a red LED light up when an object comes within 20cm of the corresponding sensor. The ultimate goal is to have code that makes intelligent decisions on which LED to light up based on the the distances of objects from each sensor. The decision making algorithm needs to know the readings from each sensor continuously or as much as possible. Obviously, this is much more advanced.
Here is the code I have now, which I haven't had a chance to test yet. I'm sure it is not the most efficient. Please help me improve this code, so that I can more easily update it for the decision making algorithm. (The code only accounts for 2 sensors right now to make the wiring easier)
#define trigPin 13
#define echoPin1 12
#define echoPin2 4
#define red1 11
#define green1 10
#define red2 3
#define green2 2
long threshold = 20; //Distance under which the red LEDs will turn on
long trigTime, duration, distance;
void setup() {
pinMode(trigPin, OUTPUT);
pinMode(red1, OUTPUT);
pinMode(green1, OUTPUT);
pinMode(red2, OUTPUT);
pinMode(green2, OUTPUT);
attachInterrupt(echoPin1, sensor1, RISING);
attachInterrupt(echoPin2, sensor2, RISING);
}
void loop() {
//Trigger sensors
noInterrupts();
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
trigTime = micros();
interrupts();
while(micros() - trigTime < 100000) {
//Wait for interrupts
//Trigger sensors 10 times per second
}
}
void sensor1(){
//Calculate sound signal duration
duration = micros() - trigTime;
//Calculate distance
distance = duration/58;
//If object within threshold of sensor 1
if (distance < threshold) {
digitalWrite(red1,HIGH); //Turn red LED on
digitalWrite(green1,LOW); //Turn green LED off
}
else {
digitalWrite(red1,LOW); //Turn red LED off
digitalWrite(green1,HIGH); //Turn green LED on
}
}
void sensor2(){
//Calculate sound signal duration
duration = micros() - trigTime;
//Calculate distance
distance = duration/58;
//If object within threshold of sensor 2
if (distance < threshold) {
digitalWrite(red2,HIGH); //Turn red LED on
digitalWrite(green2,LOW); //Turn green LED off
}
else {
digitalWrite(red2,LOW); //Turn red LED off
digitalWrite(green2,HIGH); //Turn green LED on
}
}
class SensorObject
{public:
int echoPin;
int redPin;
int greenPin;
bool pinged; //used to record if current ping has been recieved yet
SensorObject(int echoPin, int redPin, int greenPin);
void begin();
};
SensorObject::SensorObject(int ep, int rp, int gp)
{
echoPin=ep;
redPin=rp;
greenPin=gp;
}
void SensorObject::begin()
{
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
attachInterrupt(echoPin, pingReturn, RISING);
}
#define sensorCount 2
#define trigPin 13
SensorObject mySensors[sensorCount]={
SensorObject(12,11,10),
SensorObject(4,3,2)
};
void setup()
{
for (int n=0; n< sensorCount; n++)
mySensors[n].begin();
}
long threshold = 20; //Distance under which the red LEDs will turn on
long trigTime, duration, distance;
void loop() {
//Trigger sensors
noInterrupts();
for(int n=0;n<sensorCount;n++)
mySensors[n].pinged=false;
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
trigTime = micros();
interrupts();
while(micros() - trigTime < 100000) {
//Wait for interrupts
//Trigger sensors 10 times per second
}
}
void pingReturn(){
//Calculate sound signal duration
duration = micros() - trigTime;
//Calculate distance
distance = duration/58;
//find out which sensor has recieved the "ping"
//(ignore any that have already responded for this ping)
for( int n=0;n<sensorCount;n++)
if(digitalRead (mySensors[n].echoPin) && (!mySensors[n].pinged))
{mySensors[n].pinged=true;
if (distance < threshold)
{
digitalWrite(mySensors[n].redPin,HIGH); //Turn red LED on
digitalWrite(mySensors[n].greenPin,LOW); //Turn green LED off
}
else
{
digitalWrite(mySensors[n].redPin,LOW); //Turn red LED off
digitalWrite(mySensors[n].greenPin,HIGH); //Turn green LED on
}
}
}
Thank you KenF! I like how organized this code is and how easy it is to use an array of sensor objects. I do have a concern with the line "if(digitalRead (mySensors[n].echoPin) && (!mySensors[n].pinged))" in the pingReturn() interrupt, though. I'm not sure if the echoPin for the sensor that called the interrupt would be high at the time this if statement were called. Also, other echoPins may be high at the same time. The document that shows how to communicate with the sensors says that the echoPin stays high for a certain amount of time and this is another way to measure the distance read by the sensor. That is why in my code I call the interrupt with the RISING logic level. Calling the interrupt for HIGH resulted in flickering LEDs which I believe was due to the interrupt being called when it wasn't supposed to be.
I think I would add a newDistance and oldDistance variable to the sensor objects in order to implement the decision making algorithm. I'm not sure where I'd put the algorithm though. Maybe partially in the interrupt and partially in the loop.
I wonder if this sensor/interrupt relationship can be improved by implementing the Publish/Subscribe design pattern. I will look into that now.
If you're talking about sending multiple triggers from ultrasonic boards how are you going to distinguish
the echo of one from the other if you trigger them all at the same time . How does that work. One sensor
doesn't know the other exists and cannot distinguish it's own trigger from another sensor's. Did I misun-
understand what you were saying ?
raschemmel:
If you're talking about sending multiple triggers from ultrasonic boards how are you going to distinguish
the echo of one from the other if you trigger them all at the same time . How does that work. One sensor
doesn't know the other exists and cannot distinguish it's own trigger from another sensor's. Did I misun-
understand what you were saying ?
Each sensor has one trigger and one echo pin. By connecting all 8 trigger pins to one Arduino output, all the sensors can be triggered at once. Each echo pin is attached to separate interrupt pins. This makes it possible to read from all the sensors ten times per second rather than read each about 1 time per second. The problem is an interrupt might be missed if another interrupt already running. That is one major problem I'm trying to solve. The decision making algorithm must react as quick as possible to the distance changes.
palomban:
I'm not sure if the echoPin for the sensor that called the interrupt would be high at the time this if statement were called. Also, other echoPins may be high at the same time. p.
the "pinged" variable is set LOW for each sensor just before you send out a ping.
When an interrupt is first called, there is no way of knowing which pin caused the interrupt. So it searches through ALL the sensors echo pins to find the one that is CURRENTLY responding.
Having found the sensor that is currently responding it then sets its "pinged" variable (for this sensor) to HIGH so that any further interrupts ignore this sensor.
KenF:
the "pinged" variable is set LOW for each sensor just before you send out a ping.
When an interrupt is first called, there is no way of knowing which pin caused the interrupt. So it searches through ALL the sensors echo pins to find the one that is CURRENTLY responding.
Having found the sensor that is currently responding it then sets its "pinged" variable (for this sensor) to HIGH so that any further interrupts ignore this sensor.
Thank you very much for the help KenF. I have changed the code to what I think should functionally work best for me. I'm pretty sure the way your code determined which sensor called the interrupt is inaccurate, because multiple echo pins can be high while pinged is set to false, if an interrupt is missed. I changed the code so that as little code as possible is in each interrupt. This way the interrupt should be exited before the other sound pings travel even just a centimeter.
If anyone can help me reduce the code to just one interrupt for all the sensors combined, that would be appreciated. It's a pain that the attachInterrupt() function can't take a char array or something for the ISR name.
#define trigPin 13 //The pin used to trigger the sensors
const long threshold = 20; //Distance under which the red warning LEDs will turn on
const int sensorCount = 2; //The number of sensors
long trigTime; //The time at which the sensors were most recently triggered
//A SensorObject will be created for each of the sensors
class SensorObject{
public:
int echoPin;
int redPin;
int greenPin;
long distance;
bool echoReceived;
SensorObject(int echoPin, int redPin, int greenPin);
void begin();
};
//SensorObject constructor
SensorObject::SensorObject(int ep, int rp, int gp){
echoPin = ep;
redPin = rp;
greenPin = gp;
distance = 0;
echoReceived = false;
}
//Begin method used in setup() to set pin modes
void SensorObject::begin(){
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
}
//Create a global array of SensorObjects
SensorObject sensorArray[sensorCount] = {
//SensorObject(echoPin, redPin, greenPin)
SensorObject(12,11,10),
SensorObject(4,3,2)
};
void setup(){
for (int n = 0; n < sensorCount; n++)
sensorArray[n].begin();
attachInterrupt(sensorArray[0].echoPin, sensor1, RISING);
attachInterrupt(sensorArray[1].echoPin, sensor2, RISING);
}
//Main loop runs approximately 10 times per second
void loop() {
//Trigger sensors (10 times per second)
for(int n = 0; n < sensorCount; n++){
sensorArray[n].echoReceived = false;
}
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
trigTime = micros();
interrupts();
//Read sensors (10 times per second)
while(micros() - trigTime < 100000) {/*Wait for interrupts for 100ms*/}
//Set LEDs (10 times per second)
noInterrupts();
setLEDs();
}
// This function updates the LEDs when called
void setLEDs(){
for(int n = 0; n < sensorCount; n++){
if((sensorArray[n].echoReceived == true) && (sensorArray[n].distance < threshold)){
digitalWrite(sensorArray[n].redPin,HIGH); //Turn red LED on
digitalWrite(sensorArray[n].greenPin,LOW); //Turn green LED off
}
else{
digitalWrite(sensorArray[n].redPin,LOW); //Turn red LED off
digitalWrite(sensorArray[n].greenPin,HIGH); //Turn green LED on
}
}
//Insert decision algorithm here
}
//Sensor 1 interrupt
void sensor1(){
//Calculate sound signal duration
long duration = micros() - trigTime;
//Calculate distance between sensor and object
sensorArray[0].distance = duration/58;
//Echo is received
sensorArray[0].echoReceived = true;
}
//Sensor 2 interrupt
void sensor2(){
//Calculate sound signal duration
long duration = micros() - trigTime;
//Calculate distance between sensor and object
sensorArray[1].distance = duration/58;
//Echo is received
sensorArray[1].echoReceived = true;
}
I did a bit of reading up on this and it appears there's no simple solution. An ISR cannot take parameters (so therefore it's not possible to specify which pin caused the interrupt). It's also not possible to use a class member function as an ISR (unless it's static, which defeats the object of the exercise).
This is why my implementation goes through the process of checking each possible pin to ascertain which caused the interrupt. Once found, it sets it's "pinged" attribute so that it can be ignored in subsequent interrupt calls.
Although, one slight bug I've noticed is that the declaration for "pinged" in the class definition should be volatile. So where I have bool pinged this should read volatile bool pinged
The Arduino Due is very fast, so the use of delayMicroseconds(10); can stall the MCU for most of the iteration time.
This example (untested) uses no interrupts and no delays.
The range is determined by measuring the echo pulse duration (pulse width).
To add additional sensors, just include the pin numbers.
const byte trigPin = 13;
const byte echoPin[] = {12, 4}; // add pins for additional sensors
const byte green[] = {10, 2}; // add pins for additional sensors
const byte red[] = {11, 3}; // add pins for additional sensors
const byte qty = sizeof(echoPin);
const double echoVelocity = 19.68503935; // velocity (39.3700787 inches/sec) / 2
unsigned long trigInterval = 100000, trigPreviousMicros;
unsigned long echoStart[qty], echoDuration[qty];
double echoRange[qty], threshold = 20; //Distance under which the red LEDs will turn on
boolean echoState[qty], echoStatePrevious[qty];
void setup() {
pinMode(trigPin, OUTPUT);
for (int i = 0; i < qty; i++) {
pinMode(echoPin[i], INPUT);
pinMode(red[i], OUTPUT);
pinMode(green[i], OUTPUT);
echoStart[i] = 0;
echoDuration[i] = 0;
echoRange[i] = 0.0;
echoState[i] = LOW;
echoStatePrevious[i] = LOW;
}
}
void loop() {
echoMeasure();
}
void echoMeasure() {
if (micros() - trigPreviousMicros > trigInterval) { // trigger sensors every 100ms
digitalWrite(trigPin, HIGH);
if ((micros() - trigPreviousMicros) > (trigInterval + 10)) { // 10µs duration
digitalWrite(trigPin, LOW);
trigPreviousMicros = micros();
}
}
for (int i = 0; i < qty; i++) {
echoState[i] = digitalRead(echoPin[i]); // read the inputs
if ((echoState[i] == HIGH) && (echoStatePrevious[i] == LOW)) { // if echo rising
echoStart[i] = micros();
echoStatePrevious[i] = echoState[i];
}
if ((echoState[i] == LOW) && (echoStatePrevious[i] == HIGH)) { // if echo falling
echoDuration[i] = micros() - echoStart[i]; // echo duration (micros)
echoRange[i] = echoDuration[i] * echoVelocity; // echo range (inches)
echoStatePrevious[i] = echoState[i];
}
if (echoRange[i] < threshold) {
digitalWrite(red[i], HIGH); // Turn red LED on
digitalWrite(green[i], LOW); // Turn green LED off
}
else {
digitalWrite(red[i], LOW); // Turn red LED off
digitalWrite(green[i], HIGH); // Turn green LED on
}
}
}