Two functions on same GPIO

The following code uses 3 LEDs. Each LED has its timer when it should be turned on and off (turn on every 2min20sec, remain turned on for 20 sec, then turn off). These LEDs are connected to GPIO4, GPIO 2 and GPIO17. Is it possible to both let the LED on GPIO4 be controlled by the timer AND remotely from a web-server?

For example, if I turn on the LED via the web-server, the timer should not reset. It should still turn on every 2min and 20 seconds. If I turn on the LED via the web-server and then turn it off and this turning off coincidences with the 20 seconds it should be turned on - it should remain turned on. Basically, can these two be independent from each other?

#include <Arduino.h>

// Pin definitions
const int ledPin1 = 4;  // GPIO 4
const int ledPin2 = 2;  // GPIO 2
const int ledPin17 = 17;  // GPIO 17

const byte TimeOffset = 0;
const byte WaitSwitchOnLed1 = 1;
const byte WaitSwitchOffLed1 = 2;
const byte WaitSwitchOnLed2 = 3;
const byte WaitSwitchOffLed2 = 4;

char myStateNames[][32] = {
  "TimeOffset",
  "WaitSwitchOnLed1",
  "WaitSwitchOffLed1",
  "WaitSwitchOnLed2",
  "WaitSwitchOffLed2"
};

byte myStateVar;
unsigned long StartMillis;
unsigned long myWaitTimerVar;

//######### To be executed once to give time ########
//######### for preparation of the camera    ########
void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");

  // Initialize LED pins as output
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin17, OUTPUT);

  // Code to be executed only once

  // Turn off all the lights
  Serial.println("LED off");
  digitalWrite(ledPin1, LOW);
  digitalWrite(ledPin2, LOW);
  digitalWrite(ledPin17, LOW);
  delay(1000);
  
  // Mount the lights on place
  Serial.println("LED On");
  digitalWrite(ledPin1, HIGH);
  digitalWrite(ledPin2, HIGH);
  digitalWrite(ledPin17, HIGH);
  delay(180000); // 3min to do this

  // Turn off the main lights,
  // Blinking blue indicates it starts soon 
  digitalWrite(ledPin1, LOW);
  digitalWrite(ledPin2, LOW);
  

  Serial.println("Blink LED17");
  for (int i = 0; i < 3; i++) {   
    digitalWrite(ledPin17, LOW);
    delay(1000);
    digitalWrite(ledPin17, HIGH);
    delay(3000);
  } // total 12 sec - mounting has to be finished
  
  Serial.println("2 Blink LED17");
  for (int i = 0; i < 10; i++) {    
    digitalWrite(ledPin17, LOW);
    delay(1000);
    digitalWrite(ledPin17, HIGH);
    delay(1000);
  } // total 20 sec - camera has to be ready
  
  Serial.println("3 Blink LED17");
  for (int i = 0; i < 10; i++) {     
    digitalWrite(ledPin17, LOW);
    delay(500);
    digitalWrite(ledPin17, HIGH);
    delay(500);
  } // total 10 sec - camera has to run after the 8th blink
    // or 2-3 seconds after the last blink!
  //∞∞∞∞∞∞∞∞∞∞∞∞ -- ∞∞∞∞∞∞∞∞∞∞∞∞
  //∞∞∞∞∞∞∞∞∞∞∞∞ -- ∞∞∞∞∞∞∞∞∞∞∞∞

  
  myStateVar = TimeOffset; // Go to first case (case 0)
  Serial.println("Exiting Setup(), entering Loop()");
  // Indicating that it enters the loop
  myWaitTimerVar = millis();
}

//########### Time-keeping function ###########
//###########                       ########### 
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}
//∞∞∞∞∞∞∞∞∞∞∞∞ End of Time-keeping ∞∞∞∞∞∞∞∞∞∞∞∞
//∞∞∞∞∞∞∞∞∞∞∞∞      function       ∞∞∞∞∞∞∞∞∞∞∞∞ 

//########### Switch-Case function ###########
//###########                      ###########

void myCases() {

  switch (myStateVar) {

    case TimeOffset:
      if ( TimePeriodIsOver (myWaitTimerVar,10000) ) { 
        myStateVar = WaitSwitchOnLed1;
      } 


        // as the next step starts immidiately 
        // you don't need to update myWaitTimerVar
        // because updating of myWaitTimerVar 
        // is done INSIDE function TimePeriodIsOver automatically
      
      break;

    case WaitSwitchOnLed1:
      if ( TimePeriodIsOver (myWaitTimerVar,130000) ) { 
        digitalWrite(ledPin1, HIGH);
        myStateVar = WaitSwitchOffLed1;
      }
      break;
    
    case WaitSwitchOffLed1: 
      if ( TimePeriodIsOver (myWaitTimerVar,20000) ) { 
        digitalWrite(ledPin1, LOW);
        myStateVar = WaitSwitchOnLed2;
      }
      break;

    case WaitSwitchOnLed2:
      if ( TimePeriodIsOver (myWaitTimerVar,130000) ) { 
        digitalWrite(ledPin2, HIGH);
        myStateVar = WaitSwitchOffLed2;
      }
      break;

    case WaitSwitchOffLed2:
      if ( TimePeriodIsOver (myWaitTimerVar,20000) ) { 
        digitalWrite(ledPin2, LOW);
        Serial.println("one cycle finished");
        Serial.println();
        Serial.println();
        myStateVar = WaitSwitchOnLed1;
      }
      break;
  }
}

void printStateIfChanged() {
  static byte last_myStateVar; // must have static to stay alive over function-calls

  // check if myStateVar has changed since last printing 
  if (last_myStateVar != myStateVar ) {
    Serial.print("state changed from ");
    Serial.print(myStateNames[last_myStateVar]);
    Serial.print(" to ");
    Serial.println(myStateNames[myStateVar]);
    
    last_myStateVar = myStateVar; // update variable
  }
}
//∞∞∞∞∞∞∞∞∞∞∞∞ End of Switch-case ∞∞∞∞∞∞∞∞∞∞∞∞
//∞∞∞∞∞∞∞∞∞∞∞∞     function      ∞∞∞∞∞∞∞∞∞∞∞∞
 
//########### The Loop ###########
//###########          ###########
void loop() {
  printStateIfChanged();
  myCases();
}
//∞∞∞∞∞∞∞∞∞∞∞∞ -- ∞∞∞∞∞∞∞∞∞∞∞∞

// last tested 13. Juli 2023

Short answer: yes, it's perfectly possible. Just a matter of coding.

2 Likes

Of course.

The LED is just off or on - it neither knows nor cares what multitude of conditions your code might be evaluating in order to decide whether to turn it on or off.

look this over to see how LEDs can be handled more generically with less code

i use buttons instead of a web I/F to toggle an LED state outside the timer


struct Led {
    const byte PinLed;
    const byte PinBut;

    const unsigned long MsecOn;
    const unsigned long MsecOff;

    unsigned long msecLst;
    unsigned long msecPeriod;

    byte butLst;
};

Led leds [] = {
    { 4,  A1, 1000, 5000 },
    { 2,  A2, 1000, 5000 },
    { 17, A3, 1000, 5000 },
};

const int Nled = sizeof(leds) / sizeof(Led);

enum { Off = HIGH, On = LOW };

// -----------------------------------------------------------------------------
void loop ()
{
    unsigned long msec = millis ();

    Led *p = leds;
    for (int n = 0; n < Nled; n++, p++)  {
        // check for led timeout
        if (msec - p->msecLst >= p->msecPeriod)  {
            p->msecLst = msec;

            if (On == digitalRead (p->PinLed))  {
                digitalWrite (p->PinLed, Off);
                p->msecPeriod = p->MsecOff;
            }
            else  {
                digitalWrite (p->PinLed, On);
                p->msecPeriod = p->MsecOn;
            }
        }

        // check for button press
        byte but = digitalRead (p->PinBut);
        if (p->butLst != but)  {
            p->butLst = but;
            delay (20);         // debounce
            if (LOW == but)  {
                Serial.println (p->PinBut);
                digitalWrite (p->PinLed, ! digitalRead (p->PinLed));  // toggle
            }
        }
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);

    Led *p = leds;
    for (int n = 0; n < Nled; n++, p++)  {
        pinMode      (p->PinLed, OUTPUT);
        digitalWrite (p->PinLed, On);
        p->msecPeriod = p->MsecOn;
        p->msecLst    = -random (0, 1500);

        Serial.println (p->msecLst);

        pinMode      (p->PinBut, INPUT_PULLUP);
        p->butLst = digitalRead (p->PinBut);
    }
}

Hello zeeko1

consider

// https://forum.arduino.cc/t/two-functions-on-same-gpio/1149995
#define ProjectName "Two functions on same GPIO\n"
// make names
enum IntervalModes {Off, On, OnOffMax};
enum Names {One, Two, Three};
enum TimerControl {Halt, Run};
// make variables
constexpr uint8_t LedPins [] {9, 10, 11};
// make structures
struct WEBBLINKER
{
  uint8_t pin;
  uint32_t intervalMillis[OnOffMax];
  uint32_t previousMillis;
  uint8_t control;
  void startTimer (uint32_t currentMillis)
  {
    previousMillis = currentMillis;
    control = Run;
    digitalWrite(pin, HIGH);
  }
  void stopTimer ()
  {
    control = Halt;
    digitalWrite(pin, LOW);
  }
  void runTimer (uint32_t currentMillis)
  {
    if (currentMillis - previousMillis >= intervalMillis[digitalRead(pin)] and control)
    {
      previousMillis = currentMillis;
      digitalWrite(pin, digitalRead(pin) ? LOW : HIGH);
    }
  }
} webBlinkers[] {
  {
    LedPins [One], {5000, 500}, 0, Halt,
  },
  {
    LedPins [Two], {2000, 200}, 0, Halt,
  },
  {
    LedPins [Three], {1000, 100}, 0, Halt,
  },
};

// make support
void heartBeat(int LedPin, uint32_t currentMillis)
{
  static bool setUp = false;
  if (!setUp) pinMode (LedPin, OUTPUT), setUp = !setUp;
  digitalWrite(LedPin, (currentMillis / 500) % 2);
}

void setup()
{
  Serial.begin(115200);
  Serial.println(ProjectName);
  for (auto webBlinker : webBlinkers) pinMode(webBlinker.pin, OUTPUT);
}
void webEmulator(uint32_t currentMillis)
{
  static uint8_t setUp = LOW;
  if (setUp == LOW)
  {
    Serial.println("web interface emulator");
    Serial.println("[1] channel 1 and control off");
    Serial.println("[2] channel 1 and control on");
    Serial.println("[3] channel 2 and control off");
    Serial.println("[4] channel 2 and control on");
    Serial.println("[5] channel 3 and control off");
    Serial.println("[6] channel 3 and control on");
    setUp = HIGH;
  }
  if (Serial.available() > 0)
  {
    switch (Serial.read())
    {
      case '1':
        webBlinkers[One].stopTimer();
        break;
      case '2':
        webBlinkers[One].startTimer(currentMillis);
        break;
      case '3':
        webBlinkers[Two].stopTimer();
        break;
      case '4':
        webBlinkers[Two].startTimer(currentMillis);
        break;
      case '5':
        webBlinkers[Three].stopTimer();
        break;
      case '6':
        webBlinkers[Three].startTimer(currentMillis);
        break;
    }
  }
}
void loop()
{
  uint32_t currentMillis = millis();
  heartBeat(LED_BUILTIN, currentMillis);
  webEmulator(currentMillis);
  for (auto &webBlinker : webBlinkers) webBlinker.runTimer(currentMillis);
}

Have a nice day and enjoy coding in C++.

1 Like

@gcjr @paulpaulson

if a user is asking such a basic question like " Two functions on same GPIO "
Do you really think it is appropriate to post code that is on that level like you have posted?
Do you think it is helpful to post such a code without explaining comments?

This description is too short to be clearly/unequivocal because not all details are defined

00:00 timer starts waiting 2:20 with LED off
02:10 web-server-command turns on LED not resetting the timer means
02:20 timer executes switching on the LED (which is already on by webserver-command)
02:40 after 20 seconds since timer switched on, the LED timer switches off LED
02:42 web-server-command turns on LED not resetting the timer means
05:00 after 2:20 timer executes switching on the LED (which is already on by webserver-command)
05:03 webserver switches off the LED
05:20 after 20 seconds timer switches off LED (which is already switched off by webserver-command

0:00 timer starts waiting 2:20 with LED off
1:05 webserver-command switch LED on
02:19 webserver-comand switch LED off
02:20 after waiting 2:20 timer switches LED on

now what does

mean
stay always on = deactivating the timer? stay on fo 20 seconds?

slightly different timing
0:00 timer starts waiting 2:20 with LED off
1:05 webserver-command switches LED on
02:20,00 after waiting 2:20 timer switches LED on
02:20,01 webserver-command for switching LED off is received
but as the "20_seconds _LED_is_on"-period is active
shall the LED stay on? or shall the webserver-command be able to switch the LED off ?

which would mean that the timer is the one that is able to override the webserver-commands?
And this would mean timer and webserver can act independant from each other but the timer has the last word = higher priority

best regards Stefan

Do you expect an answer realy?

1 Like

yes. what needs to be explained? the OP's code had similar structures

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.