Arduino Uno Controlling multiple LEDs with one button for different amounts of time - while-loop

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
}
}
}
}

Related

Servo motor won't work for a specific code

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
}
}

User input stuck in loop

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.

How do you make a button only send one input to a source while being pushed down and not send another one until the button is let go and repressed?

In a basic variable and Button setup on an Arduino software, the idea is that if you press a button, you gain one on a variable. Theoretically, holding the button shouldn't cause the variable to increase more than one until the release and repressing of the button. However, I could not make this happen. How is this supposed to be done?
I tried using an else around a delay (because the adding one to a variable code was inside an "if" statement) so it would not delay unless the button and released and would not count multiple button inputs, but this resulted in a larger increase when a button was clicked rather than just a one increase.
const int buttonPin = 8;
int number = 0;
int numbertwo = 0;
int buttonState = 0;
void setup()
{
pinMode(buttonPin, INPUT);
}
void loop()
{
buttonState = digitalRead(buttonPin);
if (buttonState == LOW){
numbertwo = ++number;
delay(100);
}
(Can use any method to determine variable, like on an LCD)
As mentioned above, I expected the variable to only increase by one when a button is pressed and not to continue increasing until the button is released and re-pressed, but what actually happened was the variable kept increasing in value as the button was held.
I'm assuming you want the variable "number" increase by 1 per press. I think you are looking for something like this:
const int buttonPin = 8;
int number = 0;
int buttonState = 0;
void setup() {
pinMode(buttonPin, INPUT);
}
void loop()
{
buttonState = digitalRead(buttonPin);
if (buttonState == LOW){
++number;
// This loop is to make sure not to count up until button is released
do {
delay(5);
buttonState = digitalRead(buttonPin);
} while (buttonState == LOW);
}
}
If you don't like the loop based implementation, here's a state based implementation for you:
const int buttonPin = 8;
int number = 0;
int buttonState = 0;
int prevState = -1;
void setup() {
pinMode(buttonPin, INPUT);
prevState = -1;
}
void loop()
{
buttonState = digitalRead(buttonPin);
if (buttonState == LOW && prevState != buttonState){
++number;
prevState = buttonState;
}
delay(5);
}

Breaking a while loop in Arduino

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

Arduino Protothreads seem to be shared by two buttons

I ran into a problem regarding the Protothreading library in Arduino. I have created a Button class, which represents a hardware button. Now the idea is that you can attach a ButtonListener to it, which listens to the button. If a button is pressed, then the clicked() function is called.
#include <Arduino.h>
#include <pt.h>
class ButtonListener {
public:
virtual void clicked() = 0;
virtual void longClicked() = 0;
virtual void tapped(int) = 0;
};
class Button {
static const int RECOIL_TIME = 200;
static const int LONG_CLICK_LENGTH = 1000;
private:
int _pin;
ButtonListener *_listener;
struct pt _thread;
unsigned long _timestamp = 0;
int listenerHook(struct pt *pt) {
PT_BEGIN(pt);
this->_timestamp = 0;
while (true) {
PT_WAIT_UNTIL(pt, millis() - _timestamp > 1);
_timestamp = millis();
if (&this->_listener != NULL) {
this->listenForClick();
}
}
PT_END(pt);
}
void listenForClick() {
boolean longClicked = true;
int state = digitalRead(this->_pin);
if (state == HIGH) {
unsigned long timestamp = millis();
while (true) {
longClicked = millis() - timestamp > LONG_CLICK_LENGTH;
state = digitalRead(this->_pin);
if (state == LOW) {
break;
}
}
if (&this->_listener != NULL) {
if (longClicked) {
(*this->_listener).longClicked();
}
else {
(*this->_listener).clicked();
}
}
}
}
public:
Button(int pin) {
this->_pin = pin;
}
void init() {
pinMode(this->_pin, OUTPUT);
PT_INIT(&this->_thread);
}
void setListener(ButtonListener *listener) {
this->_listener = listener;
}
void listen() {
this->listenerHook(&this->_thread);
}
};
Now I've created two implementations of ButtonListener:
class Button12Listener : public ButtonListener {
public:
void clicked() {
Serial.println("Button 12 clicked!");
}
}
The other implementation is a Button13Listener and prints "Button 13 clicked!"
Then let's run the code:
// Instantiate the buttons
Button button12(12);
Button button13(13);
void setup() {
Serial.begin(9600);
button12.init();
button13.init();
// Add listeners to the buttons
button12.setListener(new Button12Listener());
button13.setListener(new Button13Listener());
}
void loop() {
while (true) {
// Listen for button clicks
button12.listen();
button13.listen();
}
Serial.println("Loop ended.");
delay(60000);
}
I expect "Button 12 clicked!" when I click the button on pin 12, and "Button 13 clicked!" when I click the button on pin 13.
But when I try to click on any of the buttons, it is randomly printing "Button 12 clicked!" or "Button 13 clicked!" no matter what button I press.
It look like the protothreads are shared among the buttons or something.
If I check in which order the buttons are called, like this:
button12.listen();
Serial.println("listen12");
button13.listen();
Serial.println("listen13");
then the following outputs:
12
13
12
13
12
12
Thát seems okay.
So what's the problem? What have I missed?
You are completely eliminating the whole point of protothreads by having that while(true) loop in listenForClick. I would do it like this:
PT_BEGIN(thr);
while(1){
// ensure that the pin is low when you start
PT_WAIT_UNTIL(thr, digitalRead(pin) == LOW);
// wait until pin goes high
PT_WAIT_UNTIL(thr, digitalRead(pin) == HIGH);
// insert delay here for minimum time the pin must be high
this->timeout = millis() + 20; // 20 ms
// wait until the delay has expired
PT_WAIT_UNTIL(thr, this->timeout - millis() > 0);
// wait until the pin goes low again
PT_WAIT_UNTIL(thr, digitalRead(pin) == LOW);
// call the click callback
this->clicked();
}
PT_END(thr);
Then just call this thread repeatedly.
NOTE: when you have buttons connected, you would usually have pullup on the pin and have button connected between the pin and ground - so the pin is LOW when the button is down and high when it is not being pressed. This would certainly be the case on an arduino. So you would have to change the code above to wait for a negative pulse instead of a positive one. :)