I wrote this simulator simulation:
motorCW(), motorCCW() and motorStop() do the obivous.
I fed back from the simulation some limit switches you will find on digital inputs 6 and 7.
So you have control of the motor, and can read the limit switches.
I wrote a deomonstration primitive sweeper in the blank space, that is where you have full loop speed and 99.9 percent of the processor left over for the real functionality you want to dream up.
// https://wokwi.com/projects/358461378751133697
// https://forum.arduino.cc/t/help-with-attachinterrupt/1097996
# define leftSwitch 6
# define rightSwitch 7
void setup()
{
mySetup(115200);
setupSimulation();
xprintf("\nHello World.\n\n");
// your setup stuff here
pinMode(leftSwitch, INPUT_PULLUP);
pinMode(rightSwitch, INPUT_PULLUP);
// primitive: start motor moving
motorCW();
}
void loop()
{
// service the simulated motor/limit switches
loopSimulation();
// your loop stuff here, non-blocking please!
// primitive: sweep by limits, for awhile
unsigned long now = millis();
unsigned char leftLimit = digitalRead(leftSwitch);
unsigned char rightLimit = digitalRead(rightSwitch);
if (leftLimit) motorCW();
if (rightLimit) motorCCW();
if (now > 15000) {
motorStop();
xprintf("That's All,Folks!\n");
while (1); // die here
}
}
// here be dragons...
// simulate motor and limit switches
# include <Servo.h>
# define servoPin A0
# define leftLED A1
# define rightLED A2
Servo brocolli;
unsigned long now;
void setupSimulation()
{
brocolli.attach(servoPin);
brocolli.write(0);
delay(500);
brocolli.write(180);
delay(500);
brocolli.write(0);
delay(500);
brocolli.write(90);
pinMode(leftLED, OUTPUT);
pinMode(rightLED, OUTPUT);
}
void loopSimulation()
{
now = millis();
limitLEDs();
motor();
}
// primitive sweep by limit swtches
void testSimulation()
{
motorCCW();
while (digitalRead(leftLED) == LOW) {
motor();
limitLEDs();
}
motorCW();
while (digitalRead(rightLED) == LOW) {
motor();
limitLEDs();
}
}
void limitLEDs()
{
int position = brocolli.read();
digitalWrite(leftLED, position > 170 ? HIGH : LOW);
digitalWrite(rightLED, position < 10 ? HIGH : LOW);
}
static int motorDirection;
void motorCW()
{
motorDirection = -1;
}
void motorStop()
{
motorDirection = 0;
}
void motorCCW()
{
motorDirection = 1;
}
void motor()
{
static unsigned long lastMotorPoke;
static int motorPosition = 90;
now = millis();
if (!motorDirection) return;
if (now - lastMotorPoke < 50) return;
lastMotorPoke = now;
motorPosition += motorDirection << 2;
motorPosition = constrain(motorPosition, 0, 180);
brocolli.write(motorPosition);
}
//
// programmer misses printf...
void xprintf(const char *format, ...)
{
char buffer[256];
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);
va_end(args);
Serial.print(buffer);
}
// programmer forgets day of week, version
void mySetup(unsigned long bandRate)
{
Serial.begin(bandRate);
char s[] = __FILE__;
byte b = sizeof(s);
while ( (b > 0) && (s[b] != 47)) b--;
char *u = s + b + 1;
xprintf("\nHEllo WOrld!\n");
xprintf("%s %s\n\n", __DATE__, u);
xprintf("!\n");
}
Here find the simulated simulator.
You can marvel at the simulated valve motor and limit switches, or just ignore it and focus on the loop() function.
The only caveat is that the loop() must not be blocked, and must always call loopSimulation() so the mechanism supporting the simulated valve motor and limit switches is able to advance.
In the real code, you would need to call motor() at loop speed, the function that respects motor direction and makes motor move or not. It is designed to always be called, and is what gives you the "hands off" auotmaticity you seek.
Remains: an error or warning if the motor is hard driven past the limit switches - in real life, this might be implemented by a timer what says it's been a long time, should have reached the limit by now...
a7