问题
Briefly: I would like to turn on a relay for 30 seconds, after I toggle a switch on.
I'm trying to do a blinds automation at home.
I have a simple ON-OFF-ON switch, attached to an Arduino connected to Relays.
I want to turn on Relay#1 for a maximum of 30 seconds if I toggle the switch down from center. In other words, relay turns on when I switch, and when timer reaches 30 seconds relay turns off.
similarly I want to turn on Relay#2 for exactly 30 seconds if I toggle the switch up from center
And when I switch back to center, I would like the timer to reset.
I could not figure out how. Could anyone help?
I have been trying to use elapsedMillis library for this, which is a nice library that helps me avoid using Delays: http://playground.arduino.cc/Code/ElapsedMillis
However even though I could work the relays without the 30 second limitation, I couldn't figure out the code to end working of the relays. Here is my current code:
#include <elapsedMillis.h>
#define RELAY_ON 0
#define RELAY_OFF 1
#define RELAY1_TURNS_ON_BLINDS 5
#define RELAY2_SHUTS_DOWN_BLINDS 6
#define shutswitch A0
#define openswitch A1
bool LocalCommandToOpen;
bool LocalCommandToShut;
void setup() ////////SETUP////////
{
digitalWrite(RELAY1_TURNS_ON_BLINDS, RELAY_OFF);
digitalWrite(RELAY2_SHUTS_DOWN_BLINDS, RELAY_OFF);
pinMode(RELAY1_TURNS_ON_BLINDS, OUTPUT);
pinMode(RELAY2_SHUTS_DOWN_BLINDS, OUTPUT);
pinMode(shutswitch, INPUT);
pinMode(openswitch, INPUT);
} ////SETUP
void loop() { ///////LOOP
if (digitalRead(shutswitch) == 1)
{
LocalCommandToOpen = 1;
}
else
{
LocalCommandToOpen = 0;
}
if ( digitalRead(openswitch) == 1)
{
LocalCommandToShut = 1;
}
else
{
LocalCommandToShut = 0;
}
unsigned int CloseInterval = 14000;
elapsedMillis timeElapsedSinceCloseButtonPush = 0;
unsigned int OpenInterval = 14000;
elapsedMillis timeElapsedSinceOpenButtonPush = 0;
//MANUAL SWITCH OPERATION
if ( LocalCommandToShut == 1 )
{
digitalWrite(RELAY1_TURNS_ON_BLINDS, RELAY_OFF);
digitalWrite(RELAY2_SHUTS_DOWN_BLINDS, RELAY_ON);
}
else
{
digitalWrite(RELAY2_SHUTS_DOWN_BLINDS, RELAY_OFF);
}
//MANUEL DUGME ILE ACMA
if ( LocalCommandToOpen == 1)
{
digitalWrite(RELAY2_SHUTS_DOWN_BLINDS, RELAY_OFF);
digitalWrite(RELAY1_TURNS_ON_BLINDS, RELAY_ON);
}
else
{
digitalWrite(RELAY1_TURNS_ON_BLINDS, RELAY_OFF);
}
delay(500);
} /////////////////LOOP////////////////////////////////////
回答1:
One suggestion here is to use a "state machine", so that upon a switch transition, you get 'State 1'; in that state, a timer starts, and puts you in 'State 2'. In 'State 2', you check the time, and if it goes beyond X seconds, you go to 'State 3'. You can monitor the transition of a switch from low to high (or high to low), and use this to reset the state of the system.
A sample bit of code gives you an idea of how to implement this. The variable 'SystemState' is an integer, and SYSTEM_ABORT, SYSTEM_IDLE, etc. are constants.
The beauty of this is that the transition is easy to figure out. For example, if you are in the SYSTEM_WAIT state, the only thing you are looking for is the time to be greater than 5 seconds. Also, you can look at input transitions or values to set states (like SystemStopButton == 0, to set the state to be 'SYSTEM_ABORT').
// SystemStopButton is an input
void SystemStateMachine(void)
///////////////////////////////////////////////////////////////////////////////////////////
{
if (SystemStopButton == 0)
{
SystemState = SYSTEM_ABORT;
}
switch (SystemState)
{
case SYSTEM_IDLE:
{
RunPinState = OFF;
StopPinState = OFF;
if (SystemRunButton == 0)
{
SystemState = SYSTEM_START;
ShowStep();
}
break;
}
case SYSTEM_START:
{
StandardMessage = "START ";
RunPinState = ON;
StopPinState = OFF;
SystemState = SYSTEM_WAIT;
ShowStep();
break;
}
case SYSTEM_WAIT: // wait for 5 seconds
{
StandardMessage = "WAIT ";
if ((CurrentMillis - StateStepTimeMillis) > 5000)
{
SystemState = SYSTEM_RETRACT;
ShowStep();
}
break;
}
case SYSTEM_RETRACT: //
{
StandardMessage = "RETRACT";
/* motor stuff goes here... */
if ((CurrentMillis - StateStepTimeMillis) > 5000)
{
SystemState = SYSTEM_ADVANCE_TAPE_WAIT
ShowStep();
}
break;
}
// etc. etc. etc.
case SYSTEM_ABORT: //
{
StandardMessage = "ABORT";
/* motor stop stuff goes here... */
SystemState=SYSTEM_IDLE;
break;
}
default:
{
break;
}
}
}
void ShowStep(void)
/////////////////////////////////////////////////////////////////////
// show step and set time so we can keep track of time in each step
{
Serial.print("SystemState = ");
Serial.println(String(SystemState));
SetStepTime();
}
void SetStepTime(void)
/////////////////////////////////////////////////////////////////////
{
StateStepTimeMillis = CurrentMillis;
}
回答2:
You might use a state machine; this makes things a bit easier to follow.
Similar to:
Arduino Switch to Turn a Relay timer
A nice discussion of state machines is here:
Complicated state transitions: best practices
来源:https://stackoverflow.com/questions/33977086/arduino-switch-to-turn-a-relay-timer