Using Arduino Uno to control simple input/output transformations

Hi All,

I am currently working on a project which monitors the position of a lever, using IR sensors,
and decides whether the movement of the lever has fulfilled certain criteria. This is my first project with Arduino and am having trouble integrating several different inputs and using certain time-dependant criteria to control my desired outputs. I have tried my best to break down my project/code into understandable sections.

This project has three different modes defined by the state of two toggle switches. These modes are detailed below.

The simplest is mode 1:

Mode 1: If the lever breaks the back (TriggerSensor) sensor at any point, then proceeds to break the front (SuccessSensor) within any time frame, a success is recorded.

The code we are using for mode 1: (please see end of this post for full commented code for this project)

void mode1() {
  if (digitalRead(TriggerSensorIn == LOW)) {
    backPressed = true;
  }
  if (backPressed = true && digitalRead(RewardSensorIn == LOW)) {
    giveReward();
    servoReset();
    backPressed = false;
  }
}

Mode 2: Now there is a timeout phase of a defined 4 seconds (interval1). For a successful action, the lever has to be held breaking TriggerSensor for interval1, at which point a tone will sound for 2 seconds (interval2). During this interval2, if the SuccessSensor is broken, a success is recorded.

Failure within mode 2: If the TriggerSensor is unbroken before the tone sounds (i.e. during interval1), a signal is sent to reset the lever position (taken care of by a PICAXE-08 servo driver, no problems with this aspect of the project).
If the SuccessSensor is not triggered within the window of interval2 during which the tone is sounding, a signal is again sent to reset the lever position.

Code we are using for mode 2:

void mode2() {
  if (digitalRead(TriggerSensorIn == LOW)) {
    timer0 -= interval1; //reset the timer
    while (digitalRead(TriggerSensorIn == LOW)) {
      while (timer0 > interval1) {
        timer0 -= interval1; //reset the timer
        tone(10, 3000, 2000); 
        {
          goto rewardSeeking2;
        }
      }
    }

rewardSeeking2:
    if (timer0 < interval2 && digitalRead(RewardSensorIn == LOW)) {
      giveReward();
      servoReset();
    }
    else if (timer0 > interval2) {
      servoReset();
    }
  }
}

Mode 3: This is the same as mode 2, except interval1 is now of random length, between 1 adn 5 seconds. Code below:

void mode3() {
  interval1==random(1000,5000);
  if (digitalRead(TriggerSensorIn == LOW)) {
    timer0 -= interval1; //reset the timer
    while (digitalRead(TriggerSensorIn == LOW)) {
      while (timer0 > interval1) {
        timer0 -= interval1; //reset the timer
        tone(10, 3000, 2000); 
        {
          goto rewardSeeking3;
        }
      }
    }

rewardSeeking3:
    if (timer0 < interval2 && digitalRead(RewardSensorIn == LOW)) {
      giveReward();
      servoReset();
    }
    else if (timer0 > interval2) {
      servoReset();
    }
  }
}

It seems that in isolation, each mode does what it is supposed to do. However, when I put everything together (as shown below), it seems that ToneOut (the output on pin 10) is always high and I am unable to switch between the modes.

I am also trying to write the TriggerSensorIn and RewardSensorIn inputs straight to TriggerSensorOut and RewardSensorOut outputs using:

digitalWrite(TriggerSensorOut, digitalRead(TriggerSensorIn));  //This is to record to our computer the states of the trigger sensor
  digitalWrite(SuccessSensorOut, digitalRead(SuccessSensorIn));  //This is to record to our computer the states of the success sensor

at the beginning of the Loop() function, but when running the program in its entirety, these digitalWrite lines stop functioning.

Full code here:

#include <elapsedMillis.h>
const int TriggerSensorIn = 1;   //  Input from trigger sensor, LOW = broken
const int SuccessSensorIn = 2;    //  Input from success sensor, LOW = broken
const int ModeStateIn1 = 4;      //  Input 1 from switch to define paradigm mode
const int ModeStateIn2 = 5;      //  Input 2 from switch to define paradigm mode
const int SuccessSignalOut = 3;   //  Output signal of success, parallel taken off to counter module
const int ServoResetOut = 6;     //  Servo reset output signal
const int ToneOut = 10;          //  Audio stimulus
const int TriggerSensorOut = 7;  //  Output from trigger sensor for DAC board recording
const int SuccessSensorOut = 8;   //  Output from success sensor for DAC board recording
int timer = 11;
boolean backPressed = false;

elapsedMillis timer0;

#define interval1 4000
// 4 second preliminary interval
#define interval2 2000
// 2 second success interval

// the setup routine runs once when you press reset:
void setup() {
  // initialise the digital pins as inputs and outputs

  pinMode(TriggerSensorIn, INPUT);
  pinMode(SuccessSensorIn, INPUT);
  pinMode(ModeStateIn1, INPUT);
  pinMode(ModeStateIn2, INPUT);

  pinMode(timer, OUTPUT);
  pinMode(SuccessSignalOut, OUTPUT);
  pinMode(ServoResetOut, OUTPUT);
  pinMode(ToneOut, OUTPUT);
  pinMode(TriggerSensorOut, OUTPUT);
  pinMode(SuccessSensorOut, OUTPUT);


  timer0 = 0; // clear the timer at the end of startup
}

void giveSuccess() {
  // This function is the protocol for recording a success
  digitalWrite(SuccessSignalOut, HIGH);
  delay(200); //is this meant to be 200, or 2000?
  digitalWrite(SuccessSignalOut, LOW);
}

void servoReset() {
  // This function is the protocol for resetting servos after a failed action
  digitalWrite(ServoResetOut, HIGH);
  delay(200);
  digitalWrite(ServoResetOut, LOW);
}

void mode1() {
  // This function is the protocol for controlling the system during mode 1, no time constraint on the action
  if (digitalRead(TriggerSensorIn == LOW)) {
    backPressed = true;
  }
  if (backPressed = true && digitalRead(SuccessSensorIn == LOW)) {
    giveSuccess();
    servoReset();
    backPressed = false;
  }
}

void mode2() {
  if (digitalRead(TriggerSensorIn == LOW)) {
    timer0 -= interval1; //reset the timer
    while (digitalRead(TriggerSensorIn == LOW)) {
      while (timer0 > interval1) {
        timer0 -= interval1; //reset the timer
        tone(10, 3000, 2000); 
        {
          goto successSeeking2;
        }
      }
    }

successSeeking2:
    if (timer0 < interval2 && digitalRead(SuccessSensorIn == LOW)) {
      giveSuccess();
      servoReset();
    }
    else if (timer0 > interval2) {
      servoReset();
    }
  }
}


void mode3() {
  interval1==random(1000,5000);
  if (digitalRead(TriggerSensorIn == LOW)) {
    timer0 -= interval1; //reset the timer
    while (digitalRead(TriggerSensorIn == LOW)) {
      while (timer0 > interval1) {
        timer0 -= interval1; //reset the timer
        tone(10, 3000, 2000); 
        {
          goto successSeeking3;
        }
      }
    }

successSeeking3:
    if (timer0 < interval2 && digitalRead(SuccessSensorIn == LOW)) {
      giveSuccess();
      servoReset();
    }
    else if (timer0 > interval2) {
      servoReset();
    }
  }
}



void loop() {

  digitalWrite(TriggerSensorOut, digitalRead(TriggerSensorIn));  //This is to record to our computer the states of the trigger sensor
  digitalWrite(SuccessSensorOut, digitalRead(SuccessSensorIn));  //This is to record to our computer the states of the success sensor


  if (digitalRead(ModeStateIn1) == LOW && digitalRead(ModeStateIn2) == LOW) {
    // Using the two mode selective switches to define mode1
    mode1();
  }

  else if (digitalRead(ModeStateIn1) == LOW && digitalRead(ModeStateIn2) == HIGH) {
    // Using the two mode selective switches to define mode2
    mode2();
  }

  else (digitalRead(ModeStateIn1) == HIGH && digitalRead(ModeStateIn2) == LOW);  {
    // Using the two mode selective switches to define mode3
    mode3();
  }
}
  if (backPressed = true && digitalRead(RewardSensorIn == LOW)) {

= and == are not interchangeable.

Reading from pin 0 or pin 1 is probably not what you want to do.

And you have == here when it should be =

interval1==random(1000,5000);

I don't know what the elapsedMillis library does. Personally I think it would be easier to use millis() for timing as illustrated in several things at a time.

And use millis() everywhere - don't use delay().

Because I don't know elapsedMillis I suspect (but I may be wrong) that your functions mode1(), mode2() and mode3() are blocking functions - i.e. they don't return until they have done everything. You will see that the functions in several things at a time do not work like that.

...R

So Robin, you recommend using the millis() function and have every time-dependant condition depend on the relative value of this function called at a number of appropriate time points? Thanks for the insight, will have a go changing things.

And thanks to everyone who commented with corrections, still very inexperienced at this end!

All help much appreciated.

Cheers,

Pastoral

pastoral:
So Robin, you recommend using the millis() function and have every time-dependant condition depend on the relative value of this function called at a number of appropriate time points?

Another advantage of this is that everything will be very transparent to you. There will be no need to wonder if you are using a library function correctly.

...R

Robin, thanks again for your suggestion. I'm playing around with things now, but can you advise me on this aspect of my project:

I am trying to use

digitalWrite(TriggerSensorOut, digitalRead(TriggerSensorIn));  //This is to record to our computer the states of the trigger sensor
  digitalWrite(SuccessSensorOut, digitalRead(SuccessSensorIn));  //This is to record to our computer the states of the success sensor

to write the sensor inputs straight to my digidata recording device. I've tried placing the above code in the setup() but doesn't work. However, when these lines are at the beginning of loop(), they have delays in them while the mode() functions are executed. Do you have any suggestions, or do I just need to really understand your several things at the same time example? I've read through it but I'm unsure how best to approach this issue.

Cheers,

Pastoral

I've tried placing the above code in the setup() but doesn't work.

Sure it does. It may not do what you want, but that doesn't mean it doesn't work.

However, when these lines are at the beginning of loop(), they have delays in them while the mode() functions are executed.

No. There may be delays between executions while the mode() functions are executed.

Which is why we have been trying to tell you that you need to completely restructure your code so that loop() is called more often.

PaulS:
...
Which is why we have been trying to tell you that you need to completely restructure your code so that loop() is called more often.

I understand the comments better now, but I feel I have not explained the mode functions adequately.

The mode functions are supposed to take several seconds to execute; indeed, a successful lever action requires it be held in one place (TriggerSensor) for up to 5 seconds before a tone sounds for 2 seconds, in which time a successful action requires the lever to be moved to SuccessSensor. The example of SeveralThingsAtTheSameTimeRev1 has simple LED state updates, rather than complicated time-dependant conditional responses, so I am unsure how best to deal with this.

With the role of the mode functions better described, does anyone have a particular suggestion how to structure the loop() function?

The mode functions are supposed to take several seconds to execute;

I don't agree. The modes have definite starting, intermediate, and ending times (relative to the start time), but that does not mean that the functions should be blocking.

Have you looked at any state machine examples? That's what you really need. In the example you provided, you have a mode with several states. On each pass through loop(), record whatever data is needed, and then, depending on the mode and state, determine what to do.

As Robin2 suggests, develop functions for everything.

void loop()
{
   recordData();

   determineIfModeChangeShouldHappen();

   switch(mode)
   {
      case 1:
        handleStateOfModeOne();
        break;
      // Other cases go here
   }
}

Now, in determineIfModeChangeShouldHappen(), you can see if the user wants to change modes. Because other functions, like handleStateOfModeOne() are not going to be blocking, the opportunity to change modes will happen more often, as will the opportunity to log data.

Then, in handleStateOfModeOne(), you would determine whether it was time to change state, and, if so, what the new state would be and what needs to happen to make that new state active. It may be time to change state because some time has elapsed, or because some limit switch changed state, or because the temperature exceeded (or fell below) some threshold.

But, you never need to use delay().

Back to your example. Is the user supposed to be moving a lever, or is some motor/machine moving the lever?

PaulS:
I don't agree. The modes have definite starting, intermediate, and ending times (relative to the start time), but that does not mean that the functions should be blocking.
.
.
.
Back to your example. Is the user supposed to be moving a lever, or is some motor/machine moving the lever?

I see your point now and will restructure accordingly.

The machine is supposed to be moved by a user, testing reaction times and such.

If I'm using the following idea:

void loop()
{
   recordData();

   determineIfModeChangeShouldHappen();

   switch(mode)
   {
      case 1:
        handleStateOfModeOne();
        break;
      // Other cases go here
   }
}

in order to execute loop() more often, will I need nested switch functions to deal with the different facets of the different modes? Is it worth experimenting with something like FiniteStateMachine library or can I do everything I need to do without it?

in order to execute loop() more often, will I need nested switch functions to deal with the different facets of the different modes?

No. Each mode is handled in a separate function. Or should be. If you were to try to handle all states for all modes, in one function, you'd be crazy but you would also need nested switch statements.

Have look at the Thread planning and implementing a program. It has more explanation than there is in several things at a time - and generally uses the same approach.

As @PaulS says each function should do its business as quickly as possible so other things can happen. If you need to allow for the passage of time use millis() to do so without blocking.

Think how you can manage the cooking time for a chicken without needing to sit in front of the oven for 90 minutes.

...R

hello and thanks again!

I have been playing around with everyone’s helpful suggestions and have uncovered a bit of a problem:

I’ve stripped out the code to what you see below, and have a problem just executing mode1() and giveReward(). I’ve also attached some screenshots of TriggerSensorOut, RewardSensorOut and RewardSignalOut at a couple of time resolutions (200ms and 10ms x-axis gradations) to illustrate how things are going wrong.

The code seems very simple, am I doing something fundamentally wrong, or is it likely some electronic thing that is screwing up? I have things connected form the UNO very simply to a Digidata 1440A using BNC cables.

// -----LIBRARIES

// ----CONSTANTS (won't change)

const int TriggerSensorIn = 2;   //  Input from trigger sensor, LOW = broken
const int RewardSensorIn = 3;    //  Input from reward sensor, LOW = broken
const int ModeStateIn1 = 4;      //  Input 1 from switch to define paradigm mode
const int ModeStateIn2 = 5;      //  Input 2 from switch to define paradigm mode
const int RewardSignalOut = 12;   //  Output signal of reward, parallel taken off to counter module
const int ServoResetOut = 6;     //  Servo reset output signal
const int ToneOut = 10;          //  Audio stimulus
const int TriggerSensorOut = 7;  //  Output from trigger sensor for DAC board recording
const int RewardSensorOut = 8;   //  Output from reward sensor for DAC board recording

//------- VARIABLES (will change)

int backPressed = 0;

int timeoutWindow = 4000;    // 4 second preliminary interval
int rewardWindow = 2000;    // 2 second reward interval

unsigned long currentMillis = 0;

//===============

void setup() {

  // initialise the digital pins as inputs and outputs

  pinMode(TriggerSensorIn, INPUT);
  pinMode(RewardSensorIn, INPUT);
  pinMode(ModeStateIn1, INPUT);
  pinMode(ModeStateIn2, INPUT);

  pinMode(RewardSignalOut, OUTPUT);
  pinMode(ServoResetOut, OUTPUT);
  pinMode(ToneOut, OUTPUT);
  pinMode(TriggerSensorOut, OUTPUT);
  pinMode(RewardSensorOut, OUTPUT);

}

//===============

void giveReward() {
  // This function is the protocol for recording a reward
  currentMillis=millis();
  digitalWrite(RewardSignalOut, HIGH);
  if (millis() - 200 > currentMillis) {
    digitalWrite(RewardSignalOut, LOW);

  }
}


//===============

void mode1() {
  // This function is the protocol for controlling the system during mode 1, no time constraint on the action
  if (digitalRead(TriggerSensorIn == LOW)) {
    if (backPressed != 1) {
      backPressed = 1;
    }
  }

  if (backPressed == 1) {
    if (digitalRead(RewardSensorIn == LOW)) {
      giveReward();
      backPressed = 0;
    }
  }
}

//===============

void loop() {

  digitalWrite(TriggerSensorOut, digitalRead(TriggerSensorIn));  //This is to record to our computer the states of the trigger sensor
  digitalWrite(RewardSensorOut, digitalRead(RewardSensorIn));  //This is to record to our computer the states of the Reward sensor

  int msin1 = digitalRead(ModeStateIn1);
  int msin2 = digitalRead(ModeStateIn2);

  if (msin1 == LOW) {
    if (msin2 == LOW) {
      mode1();
    }
  }
  digitalWrite(RewardSignalOut, LOW);
}


//===============END

  if (digitalRead(TriggerSensorIn == LOW)) {

Classic misplaced parentheses problems. Look at this carefully, and see if you can spot the mistake.

woops, that slipped through! Will test now

 int msin1 = digitalRead(ModeStateIn1);
  int msin2 = digitalRead(ModeStateIn2);

  if (msin1 == LOW) {
    if (msin2 == LOW) {
      mode1();
    }
  }

You don't use INPUT_PULLUP so it may be that your inputs are floating at unpredictable values. Or perhaps you have an external pullup or pulldown resistor ?

Does LOW indicate the switch is pressed or not-pressed ?

The code you have will not stay in the selected mode - as soon as the switch is released it will revert to the other state. Maybe it is behaving so quickly that you can't see it.

...R

Hi All,

I have what I believe to be functional and complete code for this project. I’m currently away from work so haven’t tested it with hardware, but I’ve learnt a lot from you guys and playing around and hopefully this will be useful for someone who comes across this in the future. I will amend with the fully tested code when back.

/*
This sketch is for controlling a cued lever pushing behaviour
*/
// ----CONSTANTS (won't change)

const int TriggerSensorIn = 2;     //  Input from trigger sensor, LOW = broken
const int RewardSensorIn = 3;      //  Input from reward sensor, LOW = broken
const int ModeStateIn1 = 4;        //  Input 1 from switch to define paradigm mode
const int ModeStateIn2 = 5;        //  Input 2 from switch to define paradigm mode
const int RewardSignalOut = 12;    //  Output signal of reward, parallel taken off to counter module, servo box and DAC board reading
const int ServoResetOut = 6;       //  Servo reset output signal to servo box
const int ToneOut = 10;            //  Audio stimulus
const int TriggerSensorOut = 7;    //  Output from trigger sensor for DAC board recording
const int RewardSensorOut = 8;     //  Output from reward sensor for DAC board recording

//------- VARIABLES (will change)

int timeoutWindow = 0;             // Place holder for timeout window
int timeoutWindowDefined = 4000;   // 4 second preliminary interval
int timeoutWindowRandMin = 2000;   // Minimum window for random timeout
int timeoutWindowRandMax = 6000;   // Maximum window for random timeout
int rewardWindow = 2000;           // 2 second reward interval

int rewardStage = 0;               // Place holder for stage of giveReward function
int servoStage = 0;                // Place holder for stage of servoReset function
int mode1Stage = 0;                // Place holder for stage of mode 1
int mode2Stage = 0;                // Place holder for stage of mode 2

unsigned long rewardMillis = 0;    // Timing reference for reward delivery
unsigned long servoMillis = 0;     // Timing reference for servo reset
unsigned long mode2timeout = 0;    // Timing reference for mode 2 initial timeout
unsigned long reward2timeout = 0;  // Timing reference for reward window timeout

unsigned long currentMillis = 0;   // Timing reference for that iteration of loop()

//===============

void setup() {

 // initialise the digital pins as inputs and outputs

 pinMode(TriggerSensorIn, INPUT);
 pinMode(RewardSensorIn, INPUT);
 pinMode(ModeStateIn1, INPUT);
 pinMode(ModeStateIn2, INPUT);

 pinMode(RewardSignalOut, OUTPUT);
 pinMode(ServoResetOut, OUTPUT);
 pinMode(ToneOut, OUTPUT);
 pinMode(TriggerSensorOut, OUTPUT);
 pinMode(RewardSensorOut, OUTPUT);

}

//===============

void giveReward_s1() {
 // This function is the protocol for delivering and recording a reward, stage 1
 rewardMillis = currentMillis;
 digitalWrite(RewardSignalOut, HIGH);
 rewardStage = 1;
}

void giveReward_s2() {
 // This function is the protocol for delivering and recording a reward, stage 2
 if (rewardStage == 1) {
   if (currentMillis - 200 > rewardMillis) {
     digitalWrite(RewardSignalOut, LOW);
     rewardStage = 0;
   }
 }
}

//===============

void servoReset_s1() {
 // This function is the protocol for resetting servos after a failed action, stage 1
 servoMillis = currentMillis;
 digitalWrite(ServoResetOut, HIGH);
 servoStage = 1;
}

void servoReset_s2() {
 // This function is the protocol for resetting servos after a failed action, stage 2
 if (servoStage == 1) {
   if (currentMillis - 200 > servoMillis) {
     digitalWrite(ServoResetOut, LOW);
     servoStage = 0;
   }
 }
}

//===============

void mode1() {
 // This function is the protocol for controlling the system during mode 1, no time constraint on the action
 if (digitalRead(TriggerSensorIn == LOW)) {
   if (mode1Stage != 1) {
     mode1Stage = 1;
   }
 }

 if (mode1Stage == 1) {
   if (digitalRead(RewardSensorIn == LOW)) {
     giveReward_s1();
     mode1Stage = 0;
   }
 }
}

//===============

void mode2() {
 //This function is the protocol for controlling the system during mode 2, stage 1
 if (mode2Stage == 0) {
   if (digitalRead(TriggerSensorIn) == LOW) {
     mode2timeout = currentMillis + timeoutWindow;
     mode2Stage = 1;
   }
 }
 if (mode2Stage == 1) {
   if (currentMillis < mode2timeout) {
     if (digitalRead(TriggerSensorIn == HIGH)) {
       servoReset_s1();
       mode2Stage = 0;
     }
   }
   else if (currentMillis >= mode2timeout) {
     mode2Stage = 2;
   }
 }

 if (mode2Stage == 2) {
   tone(ToneOut, 3000);
   reward2timeout = currentMillis + rewardWindow;
   mode2Stage = 3;
 }

 if (mode2Stage == 3) {
   if (currentMillis < reward2timeout) {
     if (digitalRead(RewardSensorIn == LOW)) {
       giveReward_s1();
       mode2Stage = 0;
     }
   }
   else if (currentMillis >= reward2timeout) {
     noTone(ToneOut);
     servoReset_s1();
     mode2Stage = 0;
   }
 }
}

//===============

void loop() {

 int msin1 = digitalRead(ModeStateIn1);
 int msin2 = digitalRead(ModeStateIn2);

 currentMillis = millis();

 if (rewardStage == 0) {
   if (servoStage == 0) {
     if (msin1 == LOW) {
       mode1();
     }
     else if (msin1 == HIGH) {
       if (msin2 == LOW) {
         timeoutWindow = timeoutWindowDefined;
       }
       else {
         timeoutWindow = random(timeoutWindowRandMin,timeoutWindowRandMax);
       }
       mode2();
     }
   }
 }
 giveReward_s2();
 servoReset_s2();
}

//===============END