I have written a code with interrupts to control the servo but my servos are not working. One is meant to work with interrupt but the other one has to simply move but it is also not working. Even the serial window shows that the code is working properly ut the servos aren't moving I have checked both my servos with same connections and Sweep example of Arduino and both work fine.
#include <TimerOne.h> // Header file for TimerOne library
#include <Servo.h>
#define trigPin 12 // Pin 12 trigger output
#define echoPin 2 // Pin 2 Echo input
#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
int sound = 250;
Servo servo1; //Servos
Servo servo2;
const int button1 = 10; //Buttons
const int button2 = 8;
const int button3 = 13;
const byte interruptPin = 3;
int pos;
void setup() {
servo1.attach(9); // servo for arm
servo2.attach(5); // servo for base
pinMode(trigPin, OUTPUT); // Trigger pin set to output
pinMode(echoPin, INPUT); // Echo pin set to input
// 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);
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin),Metal_detected, HIGH);
pinMode(button1, INPUT);
pinMode(button2, INPUT);
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
servo1.write(0); // These will make the servos move to the mapped angles
servo2.write(90);
distance_checking();
if(digitalRead(button1) == HIGH)
{
while(digitalRead(button2) == LOW)
{
Serial.println("Entering Sweeping mode");
for (pos = 30; pos <= 150; pos += 1)
{ Serial.print("Angle is :");
Serial.println(pos);
servo2.write(pos);
distance_checking();
//delay(0.1); // waits 15ms for the servo to reach the position
if(digitalRead(button2) == HIGH)
{
Serial.print("Exiting Sweeping");
goto label;}
}
for (pos = 150; pos >= 30; pos -= 1) { // goes from 180 degrees to 0 degree
Serial.print("Angle is :");
Serial.println(pos);
servo2.write(pos); // tell servo to go to position in variable 'pos'
distance_checking();
//delay(0.1); // waits 15ms for the servo to reach the position
if(digitalRead(button2) == HIGH)
{
goto label;
Serial.print("Exiting Sweeping");}
}
}
}
//reset th
label:
if(digitalRead(button2) == HIGH){
servo1.write(0);
servo2.write(90);
Serial.println("press the sweeping button to enter sweeeping mode");
delay(300);
}
}
void distance_checking()
{
if (echo_duration/58 <= 20)
{
Serial.println("the servo angle is 30");
servo1.write(30);
delay(1500);
}
else {
servo1.write(0);
}
delay(500);
}
void Metal_detected()
{if(digitalRead(button2) == LOW)
{delay(5000);
Serial.print("Metal detected at servo angle:");
Serial.println(servo2.read());
servo1.write(servo1.read());
servo2.write(servo2.read());
Serial.println("Motion is stopped");
Serial.println("Press reset to go to the home position");
}
//while(digitalRead(button2) == HIGH)
// {
// Serial.print("Reseting");
// return 0;}
}
void timerIsr()
{
trigger_pulse(); // Schedule the trigger pulses
// 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;
}
}
I tested my motors with this code one by one and they worked absolutely fine:
#include <Servo.h>
Servo myservo; // create servo object to control a servo
// twelve servo objects can be created on most boards
int pos = 0; // variable to store the servo position
void setup() {
myservo.attach(9); // attaches the servo on pin 9 to the servo object
}
void loop() {
for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
}
Related
I have made this code on Arduino where the objective is to have the user type in a delay time into the serial monitor, and then the LED should be blink with that delay time. For example if I type in 1000 the LED should be turned on for 1 second then off for 1 second, then repeat.
My problem is that when the code has finished running once, it waits for a new user input, instead of continuing to blink. I think i have to take the Serial.parseInt out of the loop but i'm not sure how as every time I have tried to put it somewhere else the LED just lights up constantly.
Here is the code:
int ledPin = 13;
void setup() {
// put your setup code here, to run once:
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
Serial.print(" Enter delay time: ");
while (!Serial.available());
}
void loop() {
// put your main code here, to run repeatedly
int delayTime = Serial.parseInt();
digitalWrite(ledPin, HIGH);
delay(delayTime);
digitalWrite(ledPin, LOW);
delay(delayTime);
}
Serial.parseInt is a blocking function. That means it waits for valid serial input until it times out. Because of this, any other action in loop has to wait too. Reading user input in setup works only once though, so it never asks the user for input again.
To avoid this, you'll have to check the serial buffer, and then read each byte individually, while also doing the LED blinking in the main loop.
Another thing to avoid now, is the use of the delay function, because it also hangs the entire main loop (including the serial readings) for the given parameter time. You can still blink the LED by using timestamp intervals.
For a nice example of a non-blocking serial read, we can use this sample from the Arduino docs. Additionally, for another nice example of an LED-blinking sketch without using delay, we can use the BlinkWithoutDelay sample from the Arduino docs too.
String inString = "";
unsigned long previousMillis = 0;
int delayTime = 0;
int ledState = LOW;
int ledPin = 13;
void nonBlockingSerialReadDelayTime() {
while (Serial.available() > 0) {
int inChar = Serial.read();
if (isDigit(inChar)) {
// convert the incoming byte to a char and add it to the string
inString += (char)inChar;
}
// if you get a newline (user pressed ENTER on the serial console)
if (inChar == '\n') {
// set our new delay time
delayTime = inString.toInt();
// clear the string for new input
inString = "";
// ask user again
Serial.print(" Enter delay time: ");
}
}
}
void blinkLED() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= delayTime) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
// set the LED with the ledState of the variable
digitalWrite(ledPin, ledState);
}
}
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
while (!Serial.available());
Serial.print(" Enter delay time: ");
}
void loop() {
nonBlockingSerialReadDelayTime();
blinkLED();
}
Simply read the delay time in your setup befor you enter loop
int ledPin = 13;
int delayTime = 0;
void setup() {
// put your setup code here, to run once:
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
Serial.print(" Enter delay time: ");
while (!Serial.available());
delayTime = Serial.parseInt();
}
void loop() {
// put your main code here, to run repeatedly
digitalWrite(ledPin, HIGH);
delay(delayTime);
digitalWrite(ledPin, LOW);
delay(delayTime);
}
Sure Serial.parseInt() is blocking, but you can combine it with Serial.available()
const int ledPin = 13;
int delayTime = 1000;
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
Serial.print(" Enter delay time: ");
}
void loop() {
digitalWrite(ledPin, HIGH);
delay(delayTime);
digitalWrite(ledPin, LOW);
delay(delayTime);
if (Serial.available()) {
int temp = Serial.parseInt();
if (temp > 0) delayTime = temp;
Serial.print(" Enter delay time: ");
}
}
Of course this approach does not allow to break into a very slow blink cycle immediately, but that's a different issue.
I have a program that moves a stepper motor to the right, left and have a stop button that stops the motor. In one part of my program, a motor gradually lowers a speed and stops after a certain period of time iv.
The problem is that in this part of a program (when a motor gradually lowers a speed and then stops) I can’t stop the motor upon pressing a stop button. I understand that I need to break a while loop somehow, but using a break statement doesn't wort for me.
Do you have some ideas?
Here is my function:
/* --- STEPPER MOTOR ---*/
const int motor_step = 3;
const int motor_dir = 4;
int stepSpeed = 0;
int stepMaxSpeed = 1000;
int fadeAmount = 100;
int fadeDelay = 10;
/* ---- STOP BUTTON ---- */
int buttonStop = 5;
int stateStop=0;
void setup() {
.
.
.
stateStop = digitalRead(buttonStop);
}
void loop () {
.
.
.
myfunc();
}
void myfunc() {
if(stateStop == HIGH) {noTone(motor_step); stepSpeed = 0;}
elapsedMillis te;
unsigned int iv = 1500;
while (te < iv) {
if(stepSpeed == stepMaxSpeed) {
stepSpeed = stepSpeed+0;
tone(motor_step,stepSpeed);
digitalWrite(motor_dir,HIGH);
}
else {
stepSpeed = stepSpeed + fadeAmount;
tone(motor_step,stepSpeed);
digitalWrite(motor_dir,HIGH);
delay(fadeDelay);
}
if(stateStop == HIGH) { stepSpeed = 0; break;}
}
if(stepSpeed == stepMaxSpeed) {
while(stepSpeed>0){
stepSpeed = stepSpeed-fadeAmount;
tone(motor_step,stepSpeed);
digitalWrite(motor_dir,HIGH);
delay(fadeDelay);
if(stateStop == HIGH) { stepSpeed = 0; break;}
}
}
stepSpeed = 0;
noTone(motor_step);
digitalWrite(enable,LOW); // enable changed from HIGH
}
Your break condition does never trigger as stateStop is never being updated inside your while loop. How is your program supposed to know? It's busy running the loop and does not care about anything outside it's scope.
Check the button state inside the while loops or use interrupts
I'm having trouble displaying an image and playing audio using p5 when my arduino red led goes off. My arduino works, I just can't figure out how to get an image to pop up and audio to play when the red led goes off. My project is basically a motion sensor using an ultrasonic sensor, if that helps.
Thank you for your time.
Here's my sketch.js code:
var serial; // variable to hold an instance of the serialport library
var portName = '/dev/cu.usbmodem1421'; // fill in your serial port name here
var synth;
function preload() {
alert = loadImage('alert.jpeg');
}
function setup() {
createCanvas(1920,1080);
serial = new p5.SerialPort(); // make a new instance of the serialport
library
serial.on('list', printList); // set a callback function for the serialport
list event
serial.on('connected', serverConnected); // callback for connecting to the
server
serial.on('open', portOpen); // callback for the port opening
serial.on('data', serialEvent); // callback for when new data arrives
serial.on('error', serialError); // callback for errors
serial.on('close', portClose); // callback for the port closing
serial.list(); // list the serial ports
serial.open(portName); // open a serial port
var synth = new Tone.Synth().toMaster();
synth.triggerAttackRelease(440, 2);
var player = new Tone.Player("IntruderAlert.mp4").toMaster();
//play as soon as the buffer is loaded
player.autostart = true;
player.playbackRate = 0.8;
}
function serverConnected() {
println('connected to server.');
}
function portOpen() {
println('the serial port opened.')
}
function serialEvent() {
}
function serialError(err) {
println('Something went wrong with the serial port. ' + err);
}
function portClose() {
println('The serial port closed.');
}
Here's my .ino code:
#define trigPin 6<br>#define echoPin 7
#define GreenLED 11
#define YellowLED 10
#define RedLED 9
#define buzzer 3
int sound = 500;
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(GreenLED, OUTPUT);
pinMode(YellowLED, OUTPUT);
pinMode(RedLED, OUTPUT);
pinMode(buzzer, OUTPUT);
}
void loop() {
long duration, distance;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration/5) / 29.1;
if (distance < 50) {
digitalWrite(GreenLED, HIGH);
}
else {
digitalWrite(GreenLED, LOW);
}
if (distance < 20) {
digitalWrite(YellowLED, HIGH);
}
else {
digitalWrite(YellowLED,LOW);
}
if (distance < 5) {
digitalWrite(RedLED, HIGH);
sound = 1000;
}
else {
digitalWrite(RedLED,LOW);
}
if (distance > 5 || distance <= 0){
Serial.println("Out of range");
noTone(buzzer);
}
else {
Serial.print(distance);
Serial.println(" cm");
tone(buzzer, sound);
}
delay(300);
}
You're going to have to break your problem down into smaller steps and then approach those steps one at a time.
Can you create a sketch that just shows an image? Forget about the Arduino for a second, and just display an image. Now can you display an image when the user clicks on the screen?
Separately from that, can you write Arduino code that simply sends a message to a sketch when the red light goes off?
When you get those working independently, then it'll be much easier to think about combining them into a single application that combines both ideas.
If you get stuck, please post a MCVE of the specific step that you're stuck on, and we'll go from there. Good luck.
On my Arduino Mega 2560, I'm trying to run a motor that turns a 20-vial container (accepting int input 1-20) while regulating temperature via PID of a separate cooler. I am generally new to this field of technology so bear with me. I also have an interrupt set up for an encoder to keep track of vial position.
The void serialEvent() and void loop() are the most important portions to look at, but I decided to put the rest of the code in there just in case you needed to see it.
#include <PID_v1.h>
#include <SPI.h>
#include <TMC26XStepper.h>
#define COOL_INPUT 0
#define PIN_OUTPUT 9
TMC26XStepper tmc26XStepper = TMC26XStepper(200,5,7,6,500);
int step = 6;
int value;
int i;
char junk = ' ';
volatile long enc_count = 0;
const byte interruptPinA = 2;
const byte interruptPinB = 3;
//Define Variables we'll be connecting to
int outMax = 255;
int outMin = -145;
double Setpoint, Input, Output;
double heatInput, heatOutput, originalInput;
//Specify the links and initial tuning parameters
// AGGRESSIVE VALUES (to get to 4 deg C)
double aggKp=8.0, aggKi=3.0, aggKd=0.15;
// CONSERVATIVE VALUES (to hover around 4 deg C)
double consKp=2.5, consKi = 0.0, consKd = 1.0;
PID myPID(&Input, &Output, &Setpoint, aggKp, aggKi, aggKd, REVERSE);
void setup()
{
pinMode(step, OUTPUT);
pinMode(interruptPinA, INPUT_PULLUP);
pinMode(interruptPinB, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPinA), encoder_isr, CHANGE);
attachInterrupt(digitalPinToInterrupt(interruptPinB), encoder_isr, CHANGE);
//initialize the variables we're linked to
Input = (5.0*analogRead(COOL_INPUT)*100.0) / 1024;
Setpoint = 10.75;
myPID.SetOutputLimits(outMin, outMax);
//turn the PID on
myPID.SetMode(AUTOMATIC);
Serial.begin(115200);
tmc26XStepper.setSpreadCycleChopper(2,24,8,6,0);
tmc26XStepper.setMicrosteps(32);
tmc26XStepper.setStallGuardThreshold(4,0);
Serial.println("...started...");
tmc26XStepper.start();
Serial.flush();
Serial.println("Enter vial numbers 1-20");
}
void loop() {
Input = (5.0*analogRead(COOL_INPUT)*100.0) / 1024;
// A BUNCH OF CODE FOR TEMP REGULATION
Serial.println(Input);
delay(150);
}
void serialEvent() {
while (Serial.available() == 0) {}
i = Serial.parseInt();
Serial.print("position: ");
Serial.print(i);
Serial.print(" ");
while (Serial.available() > 0) {
junk = Serial.read();
}
if (i == 1) {
value = 0;
} else {
int num = i - 1;
value = num * 72;
}
while (enc_count != value) {
digitalWrite(6, HIGH);
delayMicroseconds(100);
digitalWrite(6, LOW);
delayMicroseconds(100);
if (enc_count == 1440) {
enc_count = 0;
}
}
Serial.println(enc_count);
}
// INFO FOR ENCODER
void encoder_isr() {
static int8_t lookup_table[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
static uint8_t enc_val = 0;
enc_val = enc_val << 2;
enc_val = enc_val | ((PIND & 0b1100) >> 2);
enc_count = enc_count + lookup_table[enc_val & 0b1111];
}
So, originally I had the two processes tested separately (vial position + encoder, then temperature regulation) and everything did exactly as it was supposed to. Now, I fused the code together and stored the vial position entry in the serialEvent() method to keep the temperature reading continuous and the vial position entry available for whenever I decided to provide input. However, when I put in a value, the program stops all together. I am able to see the number I entered (position: 5), but the Serial.println(enc_count) never gets printed. On top of the that, the temperature readings stop displaying readings.
Any thoughts? Need more information?
Thanks in advance for your help.
Scenario Overview
In the real world, I am using one button to open two mechanical valves, but one of those valves should close after a period of time that we will hard code into the sketch, and the other valve stays open for as long as the button is pushed. For proof of concept, I am lighting two LEDs as stand-ins for the valves.
Pseudocode
If Button One is pressed, Valve One should Open, and Valve Two should also Open for 200ms then Close.
Initial Solution
Within the main loop, I look for the button to be pushed as part of an if statement. When that condition is passed, I used a while loop and timer to keep "valve2" open until the time is up. LEDs work, and all is superficially great. However...
The Issue
When my partner starts putting the actual mechanicals together, valve2 doesn't open because the while loop is cycling so quickly that the voltage required to initiate the opening of the valve is not high enough.
My Question
Is it possible to isolate (without using delays) the loop & evaluation of the timer condition from the main loop in order to allow full power to be sent to the valve mechanism (or LED in this case)? Or am I overthinking this whole thing (likely the case)?
The Code
const int button1 = 2; //Pin for switch 1
const int button2 = 3; //Pin for switch 2
const int valve1 = 12; //Pin for relay 1
const int valve2 = 13; //Pin for relay 2
// variables will change:
int state1 = 0; // variable for reading the pushbutton status
int state2 = 0; // variable for reading the pushbutton status
//THIS IS THE TIME IN MILLISECONDS FOR valve2 WHEN button1 IS DEPRESSED
int valve2time = 200;
void setup() {
//switches
pinMode(button1,INPUT); //Set button1 as input
pinMode(button2, INPUT); //Set button2 as input
//relays
pinMode(valve1, OUTPUT); //Set valve1 as output
pinMode(valve2, OUTPUT); //Set valve2 as output
Serial.begin(9600);
}
void loop(){
state1 = digitalRead(button1); //state1 returns the state of button1, up or down.
state2 = digitalRead(button2); //state2 returns the state of button2, up or down.
int duration = switchTime(); //Create variable to capture duration of switch press
if (state1 == LOW && state2 == LOW){ //if no buttons are pressed
digitalWrite(valve1,LOW); //make sure valve1 is off
digitalWrite(valve2,LOW); //make sure valve2 is off
}
else if (state1 == HIGH && state2 == LOW) { //if JUST button one is pressed
digitalWrite(valve1,HIGH); //turn on valve1
while (duration <= valve2time){ //as long as the timer is below or = to what we defined up top....
digitalWrite(valve2,HIGH); //...Turn on valve2...
break; //...Then stop the while loop...
}
digitalWrite(valve2,LOW); //...and finally turn off valve2
}
else if (state2 == HIGH){ //final condition, if button two is pressed
digitalWrite(valve1,HIGH); //turn on valve1
digitalWrite(valve2,HIGH); //turn on valve2
}
}
//return the time in ms that the switch has been pressed (LOW)
long switchTime(){
//these variables are static
static unsigned long startTime = 0; //the time the switch state was first detected
static boolean state; //the current state of the switch
if(digitalRead(button1) != state){ //check to see if the switch has changed state
state = ! state; //yes, invert the state
startTime = millis(); //store the time
}
if(state == HIGH){
return millis() - startTime; //switch pushed, return time in ms
}
else{
return 0; //return 0 if the switch is not pushed (in the HIGH state)
}
}
UPDATE: The working Code
//button pins
const int BUTTON1_PIN = 2;
const int BUTTON2_PIN = 3;
const int VALVE1_PIN = 0; //mml for tiny
const int VALVE2_PIN = 1; //mml for tiny
// IO Channels - Used to simulate arduino IO
boolean inputChannels[] = {LOW, LOW}; // digital input channels "Button1" and "Button2"
boolean outputChannels[] = {LOW, LOW}; // digital output channels "Valve1" and "Valve2"
// =============================================================================================================
// You can probably ignore everything above this line
// State machine variables
const int STATE_CLOSED = 0;
const int STATE_BUTTON1_PRESSED = 1;
const int STATE_BUTTON1_RELEASED = 2;
const int STATE_BUTTON2_PRESSED = 3;
const int STATE_BUTTON2_RELEASED = 4;
int currentState = 0;
int lastState = 0;
// button debounce time in ms
unsigned long BUTTON_DEBOUNCE = 200;
unsigned long BUTTON1_PRESSED_VALVE2_FLASH = 350;
unsigned long BUTTON1_RELEASE_VALVE2_FLASH = 1000;
// state tracking arrays
boolean buttonState[] = {LOW, LOW};
boolean buttonDebounce[] = {LOW, LOW};
unsigned long buttonTimers[] = {0, 0};
unsigned long valveTimers[] = {0, 0};
void setup(){
pinMode(BUTTON1_PIN, INPUT);
digitalWrite(BUTTON1_PIN, HIGH); //MML
pinMode(BUTTON2_PIN, INPUT);
digitalWrite(BUTTON2_PIN, HIGH); //MML
pinMode(VALVE1_PIN, OUTPUT);
pinMode(VALVE2_PIN, OUTPUT);
}
/**
* Main control loop
*/
void loop() {
switch (currentState) {
case STATE_CLOSED:
handleClosedState();
lastState = STATE_CLOSED;
break;
case STATE_BUTTON1_PRESSED:
handleButton1PressedState();
lastState = STATE_BUTTON1_PRESSED;
break;
case STATE_BUTTON1_RELEASED:
handleButton1ReleasedState();
lastState = STATE_BUTTON1_RELEASED;
break;
case STATE_BUTTON2_PRESSED:
handleButton2PressedState();
lastState = STATE_BUTTON2_PRESSED;
break;
case STATE_BUTTON2_RELEASED:
handleButton2ReleasedState();
lastState = STATE_BUTTON2_RELEASED;
break;
default:;
}
}
/**
* Handler method for STATE_CLOSED
*/
void handleClosedState() {
// ensure valves are closed
if (digitalRead(VALVE1_PIN) == HIGH) {
digitalWrite(VALVE1_PIN, LOW);
}
if (digitalRead(VALVE1_PIN) == HIGH) {
digitalWrite(VALVE2_PIN, LOW);
}
// wait for button1 press
if (LOW == debouncedDigitalRead(BUTTON1_PIN, BUTTON_DEBOUNCE)) {
buttonState[BUTTON1_PIN] = LOW;
currentState = STATE_BUTTON1_PRESSED;
}
}
/**
* Handler method for STATE_BUTTON1_PRESSED
*/
void handleButton1PressedState() {
// check for button1 release
if (HIGH == debouncedDigitalRead(BUTTON1_PIN, BUTTON_DEBOUNCE)) {
currentState = STATE_BUTTON1_RELEASED;
return;
}
// open valve1
if (digitalRead(VALVE1_PIN) == LOW) {
valveTimers[VALVE1_PIN] = millis();
digitalWrite(VALVE1_PIN, HIGH);
}
// on state change open valve2
if (lastState != currentState) {
valveTimers[VALVE2_PIN] = millis();
digitalWrite(VALVE2_PIN, HIGH);
}
// and close it after 200 ms
else if ((millis() - valveTimers[VALVE2_PIN]) > BUTTON1_PRESSED_VALVE2_FLASH && digitalRead(VALVE2_PIN) == HIGH) {
digitalWrite(VALVE2_PIN, LOW);
}
// check for button2 press
if (LOW == debouncedDigitalRead(BUTTON2_PIN, BUTTON_DEBOUNCE)) {
currentState = STATE_BUTTON2_PRESSED;
}
}
/**
* Handler method for STATE_BUTTON1_RELEASED
*/
void handleButton1ReleasedState() {
// open valve2
if (lastState != currentState) {
valveTimers[VALVE2_PIN] = millis();
digitalWrite(VALVE2_PIN, HIGH);
digitalWrite(VALVE1_PIN, LOW);
}
// and close valve2 after 1000ms
else if ((millis() - valveTimers[VALVE2_PIN] > BUTTON1_RELEASE_VALVE2_FLASH)) {
digitalWrite(VALVE2_PIN, LOW);
currentState = STATE_CLOSED;
}
}
/**
* Handler method for STATE_BUTTON2_PRESSED
*/
void handleButton2PressedState() {
// open valve2
if (digitalRead(VALVE2_PIN) == LOW){
digitalWrite(VALVE2_PIN, HIGH);
digitalWrite(VALVE1_PIN, HIGH);
}
// check for button1 release
if (HIGH == debouncedDigitalRead(BUTTON1_PIN, BUTTON_DEBOUNCE)) {
currentState = STATE_BUTTON1_RELEASED;
}
// check for button2 release
else if (HIGH == debouncedDigitalRead(BUTTON2_PIN, BUTTON_DEBOUNCE)) {
currentState = STATE_BUTTON2_RELEASED;
}
}
/**
* Handler method for STATE_BUTTON2_PRESSED
*/
void handleButton2ReleasedState() {
// open valve2
if (digitalRead(VALVE2_PIN) == HIGH){
digitalWrite(VALVE2_PIN, LOW);
digitalWrite(VALVE1_PIN, HIGH);
}
// check for button1 release
if (HIGH == debouncedDigitalRead(BUTTON1_PIN, BUTTON_DEBOUNCE)) {
currentState = STATE_BUTTON1_RELEASED;
}
// check for button2 press
else if (LOW == debouncedDigitalRead(BUTTON2_PIN, BUTTON_DEBOUNCE)) {
currentState = STATE_BUTTON2_PRESSED;
}
}
/**
* Utility for debouncing input channels
* #param channel
* #param debounce
* #return
*/
boolean debouncedDigitalRead(int channel, unsigned long debounce) {
int input = digitalRead(channel);
if (input != buttonState[channel] && HIGH == buttonDebounce[channel]) {
buttonTimers[channel] = millis();
buttonDebounce[channel] = LOW;
}
if ((millis() - buttonTimers[channel]) > debounce) {
buttonState[channel] = input;
buttonDebounce[channel] = HIGH;
}
return buttonState[channel];
}
In order for the code to simultaneously a) keep looping to check the buttons, and b) achieve the desired behavior for valve2, you need a software state machine that keeps track of what valve2 is doing. In the code below, I renamed your state1 and state2 variables, so that I could introduce a new state variable that controls valve2.
The state variable is normally in the idle state.
When button1 is pressed
valve2 is turned on
a timestamp is taken
the state is changed to active
After a 200 msec delay
valve2 is turned off
the state is changed to done
The state will stay done until either button1 is released or button2 is pressed, since either of those actions resets the state to idle.
Here's what the code looks like
void loop()
{
int state = 0; //variable to keep track of valve2: 0=idle 1=active 2=done
unsigned long start; //variable to keep track of when valve2 was turned on
boolean pressed1 = (digitalRead(button1) == HIGH); //pressed1 is true if button1 is pressed
boolean pressed2 = (digitalRead(button2) == HIGH); //pressed2 is true if button2 is pressed
if ( !pressed1 && !pressed2 ) //if no buttons are pressed
{
digitalWrite(valve1,LOW); //make sure valve1 is off
digitalWrite(valve2,LOW); //make sure valve2 is off
state = 0; //clear valve2 state
}
else if ( pressed2 ) //if button2 is pressed
{
digitalWrite(valve1,HIGH); //turn on valve1
digitalWrite(valve2,HIGH); //turn on valve2
state = 0; //clear valve2 state
}
else //button1 is pressed
{
digitalWrite(valve1,HIGH); //turn on valve1
if ( state == 0 ) //if valve2 is idle
{
digitalWrite(valve2,HIGH); //turn on valve2
state = 1; //valve2 is active
start = millis(); //capture the start time
}
else if ( state == 1 ) //if valve2 is active
{
if ( millis() - start > 200 ) //has it been 200ms?
{
digitalWrite(valve2,LOW); //turn valve2 is off
state = 2; //valve2 is done
}
}
}
}