[GELÖST] Funktionen simultan ablaufen lassen

Mit einem Potentiometer mit Rastungen wähle ich unter vier (später mehr) Funktionen aus. Mit einem Taster starte ich dann die ausgewählte Funktion. Jede Funktion hat eine unterschiedliche Laufzeit (hier nur im Sekundenbereich, später Minuten). Siehe WOKWI Simulation.

Problem: Wenn ich, während eine Funktion läuft, den Potentiometerknopf drehe, wird eine neue Funktion angewählt (die Variable selection ändert sich), und auch ohne den Taster zu bedienen interferiert die neu angewählte mit der noch laufenden.

  1. Frage: Wie kann ich verhindern, dass das Drehen am Potentiometerknopf interferiert? Dann liefe jede Funktion gemäss ihrer Laufzeit zuende, bevor man mit dem Taster eine neue starten kann.

  2. Frage: Wie kann ich erreichen, dass man, während eine Funktion läuft ohne Interferenz eine andere Funktion ablaufen lassen kann?

Vielen Dank für ein paar Hinweise!

const byte pinSwitch = 8;
const byte pinsLED[] = {3, 4, 5, 6};
const size_t numLEDs = sizeof pinsLED / sizeof pinsLED[0];
const unsigned long functionIntervals[] = {6900, 4600, 2500, 1000};

byte selection = 0;
byte lastSelection = 0;
byte lastSwitchState = HIGH;
unsigned long timestamp = 0;
bool functionStart = false;

void function0(), function1(), function2(), function3();
void (*functionPointerArray[])() = {function0, function1, function2, function3};

void setup()
{
  Serial.begin(9600);
  for (int i = 0; i < numLEDs; i++)
  {
    pinMode(pinsLED[i], OUTPUT);
  }
  pinMode (pinSwitch, INPUT_PULLUP);
}

void loop()
{
  int readingPotentiometer = analogRead(A0);

  switch (readingPotentiometer)
  {
    case 0 ... 255:
      selection = 0;
      break;

    case 256 ... 511:
      selection = 1;
      break;

    case 512 ... 767:
      selection = 2;
      break;

    case 768 ...1023:
      selection = 3;
      break;
  }

  setSelectionLED();
  
  checkSwitch();

  if ((millis() - timestamp <= functionIntervals[selection]) && functionStart)
  {
    functionPointerArray[selection]();
  }
  else
  {
    functionStart = false;
  }
}

void setSelectionLED()
{
  if (lastSelection != selection)
  {
    digitalWrite(pinsLED[selection], HIGH);
    digitalWrite(pinsLED[lastSelection], LOW);
    lastSelection = selection;
  }
}

void checkSwitch()
{
  byte switchState = digitalRead (pinSwitch);
  if (switchState != lastSwitchState)
  {
    if (switchState == HIGH)
    {
      timestamp = millis();
      functionStart = true;
    }
    lastSwitchState =  switchState;
  }
}

void function0()
{
  Serial.println("function0");
}

void function1()
{
  Serial.println("function1");
}

void function2()
{
  Serial.println("function2");
}

void function3()
{
  Serial.println("function3");
}

finite state machine.
Oder du setzt ein flag das eine Funktion "läuft" und setzt dieses Flag erst bei Ende der Funktion wieder retour damit jede andere gestartet werden kann.

edit,
Nur kleinigkeiten geändert.
Den Button habe ich auf LOW umgedreht - zumindest schaltet der nach GND bin ich der Meinung.

const byte pinSwitch = 8;
const byte pinsLED[] = {3, 4, 5, 6};
const size_t numLEDs = sizeof pinsLED / sizeof pinsLED[0];
const unsigned long functionIntervals[] = {6900, 4600, 2500, 1000};

byte selection = 0;
byte lastSelection = 0;
byte lastSwitchState = HIGH;
unsigned long timestamp = 0;
bool functionStart = false;

void function0(), function1(), function2(), function3();
void (*functionPointerArray[])() = {function0, function1, function2, function3};

int8_t active = -1;  // -1 no function active

void setup()
{
  Serial.begin(9600);
  for (int i = 0; i < numLEDs; i++)
  {
    pinMode(pinsLED[i], OUTPUT);
  }
  pinMode (pinSwitch, INPUT_PULLUP);
}

void loop()
{

  selection = readPoti();
  setSelectionLED();

  checkSwitch();

  Serial.print(F("Start ")); Serial.println(active);

  if ((millis() - timestamp <= functionIntervals[active]) && functionStart)
  {
    functionPointerArray[active]();
  }
  else
  {
    Serial.println("Ende");
    functionStart = false;
    active = -1;
  }
  //delay(500);  // nur damit man was sieht
}

int readPoti()
{
  int readingPotentiometer = analogRead(A0);

  switch (readingPotentiometer)
  {
    case 0 ... 255:
      return 0;
      break;

    case 256 ... 511:
      return 1;
      break;

    case 512 ... 767:
      return 2;
      break;

    case 768 ...1023:
      return 3;
      break;
  }
  return 0;
}

void setSelectionLED()
{
  if (lastSelection != selection)
  {
    digitalWrite(pinsLED[selection], HIGH);
    digitalWrite(pinsLED[lastSelection], LOW);
    lastSelection = selection;
  }
}

void checkSwitch()
{
  byte switchState = digitalRead (pinSwitch);
  if (switchState != lastSwitchState)
  {
    if (switchState == LOW && active == -1)
    {
      timestamp = millis();
      functionStart = true;
      active = readPoti();
      Serial.print(F("Start ")); Serial.println(active);
      delay(50); // dirty debounce
    }
    lastSwitchState =  switchState;
  }
}

void function0()
{
  Serial.println("function0");
}

void function1()
{
  Serial.println("function1");
}

void function2()
{
  Serial.println("function2");
}

void function3()
{
  Serial.println("function3");
}

müsste aber schlanker auch gehen.

Ich bin nicht so von der Aufteilung überzeugt und würde das ein wenig anders bauen, aber hier mal nen Vorschlag der deinen Code als Grundlage hat.

const byte pinSwitch = 8;
const byte pinsLED[] = {3, 4, 5, 6};
const size_t numLEDs = sizeof pinsLED / sizeof pinsLED[0];
const unsigned long functionIntervals[numLEDs] = {6900, 4600, 2500, 1000};
bool functionState[numLEDs] = {false};
unsigned long timestamp[numLEDs] = {0};
byte selection = 0;
byte lastSelection = 0;
bool lastSwitchState = HIGH;

void function0(), function1(), function2(), function3();
void (*functionPointerArray[])() = {function0, function1, function2, function3};

void setup()
{
  Serial.begin(9600);
  for (uint8_t i = 0; i < numLEDs; i++)
  {
    pinMode(pinsLED[i], OUTPUT);
  }
  pinMode (pinSwitch, INPUT_PULLUP);
}

void loop()
{
  readSelection();
  setSelectionLED();
  checkSwitch();
  setFunction();
}

void setFunction()
{
  for (byte b = 0; b < numLEDs; b++)
  {
    if (millis() - timestamp[b] >= functionIntervals[b])
    {
      functionState[b] = false;
    }
    if (functionState[b] == true)
    {
      switch (b)
      {
        case 0: function0(); break;
        case 1: function1(); break;
        case 2: function2(); break;
        case 3: function3(); break;
      }
    }
  }
}

void setSelectionLED()
{
  if (lastSelection != selection)
  {
    for (byte b = 0; b < numLEDs; b++)
    {digitalWrite(pinsLED[b], LOW);}
    digitalWrite(pinsLED[selection], HIGH);
    lastSelection = selection;
  }
}
void readSelection()
{
  unsigned int readingPotentiometer = analogRead(A0);
  switch (readingPotentiometer)
  {
    case 0 ... 255:
      selection = 0;
      break;
    case 256 ... 511:
      selection = 1;
      break;
    case 512 ... 767:
      selection = 2;
      break;
    case 768 ...1023:
      selection = 3;
      break;
  }
}
void checkSwitch()
{
  bool switchState = digitalRead (pinSwitch);
  if (switchState != lastSwitchState)
  {
    if (switchState == HIGH)
    {
      functionState[selection] = true;
      timestamp[selection] = millis();
    }
    lastSwitchState =  switchState;
  }
}
void function0()
{
  Serial.println("function0");
}
void function1()
{
  Serial.println("function1");
}
void function2()
{
  Serial.println("function2");
}
void function3()
{
  Serial.println("function3");
}

Hab ich nicht gemacht, brauchts kein debounce.

ein wenig zusammengeräumt.

// https://forum.arduino.cc/t/funktionen-simultan-ablaufen-lassen/1015805/
// V2: cleanup

const byte pinSwitch = 8;
const byte pinsLED[] = {3, 4, 5, 6};
const unsigned long functionIntervals[] = {6900, 4600, 2500, 1000};

unsigned long timestamp = 0;                              // start of function
void function0(), function1(), function2(), function3();  // prototypen
void (*functionPointerArray[])() = {function0, function1, function2, function3};

int8_t active = -1;  // -1 no function active - otherwise the index of the active function

void setup()
{
  Serial.begin(9600);
  for (auto & i : pinsLED)
  {
    pinMode(i, OUTPUT);
  }
  pinMode (pinSwitch, INPUT_PULLUP);
}

void loop()
{
  setSelectionLED();
  checkSwitch();
  run();
  //Serial.print(F("active ")); Serial.println(active);
  //delay(500);  // nur damit man was sieht
}

void run()
{
  if (active >= 0)
  {
    if ((millis() - timestamp <= functionIntervals[active]))
    {
      functionPointerArray[active](); // run the active function
    }
    else
    {
      Serial.println(F("Ende"));
      active = -1;
    }
  }
}

int readPoti()
{
  int readingPotentiometer = analogRead(A0);
  switch (readingPotentiometer)
  {
    case 0 ... 255:
      return 0;
      break;
    case 256 ... 511:
      return 1;
      break;
    case 512 ... 767:
      return 2;
      break;
    case 768 ...1023:
      return 3;
  }
  return 0;
}

void setSelectionLED()
{
  static byte selection = 0;
  static byte lastSelection = 0;
  selection = readPoti();
  if (lastSelection != selection)
  {
    digitalWrite(pinsLED[selection], HIGH);
    digitalWrite(pinsLED[lastSelection], LOW);
    lastSelection = selection;
  }
}

void checkSwitch()
{
  static byte lastSwitchState = HIGH;
  byte switchState = digitalRead (pinSwitch);
  if (switchState != lastSwitchState)
  {
    if (switchState == LOW && active == -1)
    {
      timestamp = millis();
      active = readPoti();
      Serial.print(F("Start ")); Serial.println(active);
      delay(50); // dirty debounce
    }
    lastSwitchState =  switchState;
  }
}

void function0()
{
  Serial.println("function0");
}

void function1()
{
  Serial.println("function1");
}

void function2()
{
  Serial.println("function2");
}

void function3()
{
  Serial.println("function3");
}

aber wenn man schon sich die function pointer in einem array zurecht legt, wäre es auch naheliegend statt einer int active einen function pointer zu nehmen. (Todo).

edit, nein, auch nicht schöner. Zumindest nicht bei mir

// https://forum.arduino.cc/t/funktionen-simultan-ablaufen-lassen/1015805/
// V3: function pointer

const byte pinSwitch = 8;
const byte pinsLED[] = {3, 4, 5, 6};
const unsigned long functionIntervals[] = {6900, 4600, 2500, 1000};

unsigned long timestamp = 0;                              // start of function
void function0(), function1(), function2(), function3();  // prototypen
void (*functionPointerArray[])() = {function0, function1, function2, function3};
void (*functptr)(void);         // pointer to the active function

void setup()
{
  Serial.begin(9600);
  for (auto & i : pinsLED)
  {
    pinMode(i, OUTPUT);
  }
  pinMode (pinSwitch, INPUT_PULLUP);
}

void loop()
{
  setSelectionLED();
  checkSwitch();
  run();
  //Serial.print(F("active ")); Serial.println(active);
  //delay(500);  // nur damit man was sieht
}

void run()
{
  if (functptr)
    functptr();
  else
  {
    Serial.println(F("Ende"));
    functptr = nullptr;
  }
}

int readPoti()
{
  int readingPotentiometer = analogRead(A0);
  switch (readingPotentiometer)
  {
    case 0 ... 255:
      return 0;
      break;
    case 256 ... 511:
      return 1;
      break;
    case 512 ... 767:
      return 2;
      break;
    case 768 ...1023:
      return 3;
  }
  return 0;
}

void setSelectionLED()
{
  static byte selection = 0;
  static byte lastSelection = 0;
  selection = readPoti();
  if (lastSelection != selection)
  {
    digitalWrite(pinsLED[selection], HIGH);
    digitalWrite(pinsLED[lastSelection], LOW);
    lastSelection = selection;
  }
}

void checkSwitch()
{
  static byte lastSwitchState = HIGH;
  byte switchState = digitalRead (pinSwitch);
  if (switchState != lastSwitchState)
  {
    if (switchState == LOW && !functptr)
    {
      timestamp = millis();
      byte readed = readPoti();
      functptr  = functionPointerArray[readed];
      Serial.print(F("Start ")); Serial.println(readed);
      delay(50); // dirty debounce
    }
    lastSwitchState =  switchState;
  }
}

void checkRuntime(byte actual)
{
  if ((millis() - timestamp >= functionIntervals[actual]))
  {
    Serial.print(F("ended function ")); Serial.println(actual);
    functptr = nullptr;
  }
}

void function0()
{
  checkRuntime(0);
  Serial.println("function0");
}

void function1()
{
  checkRuntime(1);
  Serial.println("function1");
}

void function2()
{
  checkRuntime(2);
  Serial.println("function2");
}

void function3()
{
  checkRuntime(3);
  Serial.println("function3");
}

Egal. Wichtig ist imho eh nur die Erkenntnis dass man die "LEDs" von der laufenden Funktion trennen muss und sie somit zwei separate Variablen haben bzw. halt nicht gemischt werden sollen.

@noiasca Danke für die Hinweise mit "return 0, 1, 2 und 3" und dem "char active -1", damit nur ein Tastendruck eine der Funktionen auslöst, und das Drehen am Potentiometerknopf nicht mehr interferiert.

das war nur reuse von deinem switch. Aber da man ihn für die selection und die Aktivierung der der jeweiligen Funktion braucht, wars mit einer separaten Funktion einfacher.

Mit geringfügigen Änderungen

  if ( !functionStart )
  {
    switch (analogRead(A0))

tut Dein Programm, was es soll. Getestet mit UNO:

const byte pinSwitch = 8;
const byte pinsLED[] = {3, 4, 5, 6};
const size_t numLEDs = sizeof pinsLED / sizeof pinsLED[0];
const unsigned long functionIntervals[] = {6900, 4600, 2500, 1000};

byte selection = 0;
byte lastSelection = 0;
byte lastSwitchState = HIGH;
unsigned long timestamp = 0;
bool functionStart = false;

void function0(), function1(), function2(), function3();
void (*functionPointerArray[])() = {function0, function1, function2, function3};

void setup()
{
  Serial.begin(9600);
  Serial.println("\nStart ...");
  for (uint8_t i = 0; i < numLEDs; i++)
  {
    pinMode(pinsLED[i], OUTPUT);
  }
  pinMode (pinSwitch, INPUT_PULLUP);
}

void loop()
{
  if ( !functionStart )
  {
    switch (analogRead(A0))
    {
      case 0 ... 255:
        selection = 0;
        break;

      case 256 ... 511:
        selection = 1;
        break;

      case 512 ... 767:
        selection = 2;
        break;

      case 768 ...1023:
        selection = 3;
        break;
    }
  }
  
  setSelectionLED();

  checkSwitch();

  if ((millis() - timestamp <= functionIntervals[selection]) && functionStart)
  {
    functionPointerArray[selection]();
  }
  else
  {
    functionStart = false;
  }
}

void setSelectionLED()
{
  if (lastSelection != selection)
  {
    digitalWrite(pinsLED[selection], HIGH);
    digitalWrite(pinsLED[lastSelection], LOW);
    lastSelection = selection;
  }
}

void checkSwitch()
{
  byte switchState = digitalRead (pinSwitch);
  if (switchState != lastSwitchState)
  {
    if (switchState == HIGH)
    {
      timestamp = millis();
      functionStart = true;
    }
    lastSwitchState =  switchState;
  }
}

void function0()
{
  Serial.println("function0");
}

void function1()
{
  Serial.println("function1");
}

void function2()
{
  Serial.println("function2");
}

void function3()
{
  Serial.println("function3");
}

Moin
Ich habe auch noch einen Sketch zum Ausprobieren.
Du mußt nur noch die Portadressen und Taskzeiten anpassen.
Viel Spass.

/* BLOCK COMMENT
  ATTENTION: This Sketch contains elements of C++.
  https://www.learncpp.com/cpp-tutorial/
  Many thanks to LarryD
  https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
  https://forum.arduino.cc/t/funktionen-simultan-ablaufen-lassen/1015805/3
  Tested with Arduino: Mega[x] - UNO [ ] - Nano [ ]
*/
#define ProjectName "Funktionen simultan ablaufen lassen"
// HARDWARE AND TIMER SETTINGS
// YOU MAY NEED TO CHANGE THESE CONSTANTS TO YOUR HARDWARE AND NEEDS
constexpr byte SelectPins[] {A0};         // portPin o---|button|---GND
constexpr byte PotPins[] {A8};
constexpr byte LedPins[] {8, 9, 10, 11};    // portPin o---|220|---|LED|---GND
// CONSTANT DEFINITION
enum InputOutput {One, Two, Three, Four};
// VARIABLE DECLARATION AND DEFINITION
unsigned long currentTime;
// forward declaration of user functions
void function0();
void function1();
void function2();
void function3();
// -- objects -----------------------------------------
struct TIMER {              // has the following members
  unsigned long duration;   // memory for interval time
  unsigned long stamp;      // memory for actual time
  int onOff;               // control for stop/start/repeat
};
struct BUTTON {             // has the following members
  const byte Pin;           // port pin
  bool stateOld;           // current state
  TIMER scan;               // see timer struct
};
enum {Low, High};
struct POT2TASK
{
  const byte LedPin;
  const byte PotPin;
  const int Range[2];
  BUTTON startKey;
  void (*task)();
  TIMER taskTimer;
};
POT2TASK pot2tasks[]
{
  {LedPins[One], PotPins[One], {0, 255}, SelectPins[One], false, 20, 0, true, function0, 2000, 0, true},
  {LedPins[Two], PotPins[One], {256, 511}, SelectPins[One], false, 20, 0, true, function1, 2000, 0, true},
  {LedPins[Three], PotPins[One], {512, 767}, SelectPins[One], false, 20, 0, true, function2, 2000, 0, true},
  {LedPins[Four], PotPins[One], {768, 1023}, SelectPins[One], false, 20, 0, true, function3, 2000, 0, true},
};
// ------------- user functions ------
void function0()
{
  Serial.println(__func__);
}
void function1()
{
  Serial.println(__func__);
}
void function2()
{
  Serial.println(__func__);
}
void function3()
{
  Serial.println(__func__);
}
// -------------------------------------------------------------------
void setup()
{
  Serial.begin(9600);
  Serial.println(F("."));
  Serial.print(F("File   : ")), Serial.println(__FILE__);
  Serial.print(F("Date   : ")), Serial.println(__DATE__);
  Serial.print(F("Project: ")), Serial.println(ProjectName);
  pinMode (LED_BUILTIN, OUTPUT);  // used as heartbeat indicator
  //  https://www.learncpp.com/cpp-tutorial/for-each-loops/
  for (auto SelectPin : SelectPins) pinMode(SelectPin, INPUT_PULLUP);
  for (auto LedPin : LedPins) pinMode(LedPin, OUTPUT);
}
void loop ()
{
  currentTime = millis();
  digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);
  for (auto &pot2task : pot2tasks)
  {
    if (currentTime - pot2task.startKey.scan.stamp >= pot2task.startKey.scan.duration)
    {
      int anaRead=analogRead(pot2task.PotPin);
      bool selected=(anaRead>=pot2task.Range[Low]&&anaRead<=pot2task.Range[High])?HIGH:LOW;
      digitalWrite(pot2task.LedPin,selected);
      pot2task.startKey.scan.stamp=currentTime;
      int stateNew=!digitalRead(pot2task.startKey.Pin);
      if  (pot2task.startKey.stateOld!=stateNew) 
      {
        pot2task.startKey.stateOld=stateNew;
        if (stateNew && selected) pot2task.taskTimer.onOff=!pot2task.taskTimer.onOff;
      }
     
    }
    if (currentTime - pot2task.taskTimer.stamp >= pot2task.taskTimer.duration && pot2task.taskTimer.onOff)
    {
      pot2task.taskTimer.stamp = currentTime;
      pot2task.task();
    }
  }
}
Ich wünsche einen geschmeidigen Tag und viel Spass beim Programmieren in C++.

@my_xy_projekt Vielen Dank für den Umbau. Damit sind beide Fragen geklärt. Auf die Idee, die functionStates und timestamps ebenfalls in ein Array zu packen, und dann permanent alle vier Funktionen durchzugehen, was deren Laufzeit und Status angeht, wäre ich nicht gekommen. Offenbar braucht es auch die function pointer nicht. Ich finde auch die Aufteilung nachvollziehbarer als meinen ersten Versuch.

@noiasca @my_xy_projekt Vielen Dank, das war lehrreich.

@agmue Danke, offenbar führen noch mehr Wege nach Rom als ich dachte. Die @my_xy_projekt Version eliminiert die function pointer, und ist besser strukturiert als mein erster Versuch.

@paulpaulson Danke, das geht allerdings über meinen Kenntnisstand hinaus, und hebe es mir besser für später auf.

Aber mit einem struct hat man halt die Konfiguration schön beisammen.

// https://forum.arduino.cc/t/funktionen-simultan-ablaufen-lassen/1015805/
// V4: function pointer, struct

const byte pinSwitch = 8;
unsigned long timestamp = 0;                              // start of function
void function0(), function1(), function2(), function3();  // prototypen
void (*functptr)(void);                                   // pointer to the active function

struct Task
{
  const uint8_t pinLed;
  const uint32_t long functionInterval;
  const uint16_t from;
  const uint16_t to;
  void (*functionPointer)(void);
};
Task task[4]
{
  {3, 6900, 0, 255, function0},
  {4, 4600, 256, 511, function1},
  {5, 2500, 512, 767, function2},
  {6, 1000, 787, 1023, function3},
};

void setup()
{
  Serial.begin(9600);
  for (auto & i : task) pinMode(i.pinLed, OUTPUT);
  pinMode (pinSwitch, INPUT_PULLUP);
}

void loop()
{
  setSelectionLED();
  checkSwitch();
  conditionalRun();
  //Serial.print(F("active ")); Serial.println(active);
  //delay(500);  // nur damit man was sieht
}

void conditionalRun()
{
  if (functptr) functptr();
}

void setSelectionLED()
{
  static uint8_t previousPin = 0;
  uint16_t readingPotentiometer = analogRead(A0);
  for (auto &i : task)
  {
    if (readingPotentiometer >= i.from && readingPotentiometer <= i.to)
      if (i.pinLed != previousPin)
      {
        digitalWrite(i.pinLed, HIGH);
        digitalWrite(previousPin, LOW);
        previousPin = i.pinLed;
        break;
      }
  }
}

void checkSwitch()
{
  static byte lastSwitchState = HIGH;
  byte switchState = digitalRead (pinSwitch);
  if (switchState != lastSwitchState)
  {
    if (switchState == LOW && !functptr)
    {
      timestamp = millis();
      uint16_t readingPotentiometer = analogRead(A0);
      for (auto &i : task)
      {
        if (readingPotentiometer >= i.from && readingPotentiometer <= i.to)
        {
          functptr  = i.functionPointer;
          break;
        }
      }
      Serial.println(F("Start "));
      delay(50); // dirty debounce
    }
    lastSwitchState =  switchState;
  }
}

void checkRuntime(byte actual)
{
  if ((millis() - timestamp >= task[actual].functionInterval))
  {
    Serial.print(F("ended function ")); Serial.println(actual);
    functptr = nullptr;
  }
}

void function0()
{
  checkRuntime(0);
  Serial.println("function0");
}

void function1()
{
  checkRuntime(1);
  Serial.println("function1");
}

void function2()
{
  checkRuntime(2);
  Serial.println("function2");
}

void function3()
{
  checkRuntime(3);
  Serial.println("function3");
}

/*
  int readPoti()
  {
  int readingPotentiometer = analogRead(A0);
  for (size_t i = 0; i < sizeof(task) / sizeof(task[0]); i++)
  {
    if (readingPotentiometer >= task[i].from && readingPotentiometer <= task[i].to) return i;
  }
  return 0;
  }
*/

Hallo Lagom

Mit dem Sketch lassen sich einfach, um nicht zu sagen sehr einfach, weitere UserFunctions hinzufügen.
Dieses geschieht über eine Erweiterung in der Struktur:

POT2TASK pot2tasks[]
{
  {LedPins[One], PotPins[One], {0, 255}, SelectPins[One], false, 20, 0, true, function0, 2000, 0, true},
  {LedPins[Two], PotPins[One], {256, 511}, SelectPins[One], false, 20, 0, true, function1, 2000, 0, true},
  {LedPins[Three], PotPins[One], {512, 767}, SelectPins[One], false, 20, 0, true, function2, 2000, 0, true},
  {LedPins[Four], PotPins[One], {768, 1023}, SelectPins[One], false, 20, 0, true, function3, 2000, 0, true},
};

Einfach mal ausprobieren und die Erfahrungen wirken lassen :slight_smile:

Ich wünsche einen geschmeidigen Tag und viel Spass beim Programmieren in C++.

@paulpaulson Danke!

@noiasca Danke, ich habe structs bisher nur in ganz einfacher Form zum Versand von Daten mit RFM69 packet radio benutzt und verstanden, aber komme sicher nochmal darauf zurück.

Wie gesagt, es hat mich nicht überzeugt, von daher das genau nochmal so, aber um einige Variablen abgespeckt.

// überarbeitet
// in den Funktionen um eine Klammerebene kleiner
// nur eine Schleife für alles
// vorherige Version oben mit dem Bleistift zu vergleichen
const byte pinSwitch = 8;
const byte pinsLED[] = {3, 4, 5, 6};
const size_t numLEDs = sizeof pinsLED / sizeof pinsLED[0];
const unsigned long functionIntervals[numLEDs] = {6900, 4600, 2500, 1000};
unsigned long timestamp[numLEDs] = {0};
byte selection = 0;

void setup()
{
  Serial.begin(9600);
  for (uint8_t i = 0; i < numLEDs; i++)
  {
    pinMode(pinsLED[i], OUTPUT);
  }
  pinMode (pinSwitch, INPUT_PULLUP);
}

void loop()
{
  readSelection();
  checkSwitch();
  
  for (byte b = 0; b < numLEDs; b++)
  {
    setSelectionLED(b);
    setFunction(b);
  }
}

void setFunction(const byte c)
{
  if (millis() - timestamp[c] < functionIntervals[c])
  {
    switch (c)
    {
      case 0: function0(); break;
      case 1: function1(); break;
      case 2: function2(); break;
      case 3: function3(); break;
    }
  }
}


void setSelectionLED(const byte d)
{
  if (d == selection)
  {digitalWrite(pinsLED[d], HIGH);}
  else
  {digitalWrite(pinsLED[d], LOW);}
}


void readSelection()
{
  unsigned int readingPotentiometer = analogRead(A0);
  switch (readingPotentiometer)
  {
    case 0 ... 255:
      selection = 0;
      break;
    case 256 ... 511:
      selection = 1;
      break;
    case 512 ... 767:
      selection = 2;
      break;
    case 768 ...1023:
      selection = 3;
      break;
  }
}

void checkSwitch()
{
  static bool lastSwitchState = HIGH;
  bool switchState = digitalRead (pinSwitch);
  if (switchState != lastSwitchState)
  {
    if (switchState == HIGH)
    {
      timestamp[selection] = millis();
    }
    lastSwitchState =  switchState;
  }
}

void function0()
{
  Serial.println("function0");
}
void function1()
{
  Serial.println("function1");
}
void function2()
{
  Serial.println("function2");
}
void function3()
{
  Serial.println("function3");
}

den Versuch in V3 mit dem functptr nehm ich zurück. Das macht nichts einfacher.

Die Struktur finde ich trotzdem in Ordnung.

// https://forum.arduino.cc/t/funktionen-simultan-ablaufen-lassen/1015805/
// V6: struct

const byte pinSwitch = 8;
unsigned long timestamp = 0;                              // start of function
void function0(), function1(), function2(), function3();  // prototypen
int8_t active = -1;

struct Task
{
  const uint8_t pinLed;
  const uint32_t long functionInterval;
  const uint16_t from;
  const uint16_t to;
  void (*functionPointer)(void);
};
Task task[]
{
  {3, 6900, 0, 255, function0},
  {4, 4600, 256, 511, function1},
  {5, 2500, 512, 767, function2},
  {6, 1000, 787, 1023, function3},
};

void setup()
{
  Serial.begin(9600);
  for (auto & i : task) pinMode(i.pinLed, OUTPUT);
  pinMode (pinSwitch, INPUT_PULLUP);
}

void loop()
{
  setSelectionLED();
  checkSwitch();
  conditionalRun();
}

void conditionalRun()
{
  if (active >= 0)
  {
    task[active].functionPointer();  // run the active task
    if ((millis() - timestamp >= task[active].functionInterval))  // check for time out
    {
      Serial.print(F("ended function ")); Serial.println(active);
      active = -1;
    }
  }
}

void setSelectionLED()
{
  static uint8_t previousPin = 0;
  uint16_t readingPotentiometer = analogRead(A0);
  for (auto &i : task)
  {
    if (readingPotentiometer >= i.from && readingPotentiometer <= i.to)
      if (i.pinLed != previousPin)
      {
        digitalWrite(i.pinLed, HIGH);
        digitalWrite(previousPin, LOW);
        previousPin = i.pinLed;
        break;
      }
  }
}

void checkSwitch()
{
  static byte lastSwitchState = HIGH;
  byte switchState = digitalRead (pinSwitch);
  if (switchState != lastSwitchState)
  {
    if (switchState == LOW && active ==-1)
    {
      timestamp = millis();
      uint16_t readingPotentiometer = analogRead(A0);
      for (int i = 0; i < sizeof(task) / sizeof(task[0]); i++)
      {
        if (readingPotentiometer >= task[i].from && readingPotentiometer <= task[i].to)
        {
          active = i;
          break;
        }
      }
      Serial.println(F("Start "));
      delay(50); // dirty debounce
    }
    lastSwitchState =  switchState;
  }
}

void function0()
{
  Serial.println("function0");
}

void function1()
{
  Serial.println("function1");
}

void function2()
{
  Serial.println("function2");
}

void function3()
{
  Serial.println("function3");
}

Bisher habe ich noch überhaupt nicht verstanden was das werden soll.
Kann also den Code selber nicht prüfen.
Verstehe zwar die Bedeutung von Simultan und interferieren, kann sie aber in diesem Kontext nicht einordnen.

Also kann ich nur unbedeutende kosmetische Vorschläge einbringen:

// https://forum.arduino.cc/t/funktionen-simultan-ablaufen-lassen/1015805/
// // V6: struct
// modified by Combie CV6.1


const byte pinSwitch = 8;
unsigned long timestamp = 0;                              // start of function
int8_t active = -1;


void function0()
{
  Serial.println("function0");
}




struct Task
{
  using FunctionPointer = void (*)(); // alias
  
  const uint8_t         pinLed;
  const uint32_t long   functionInterval;
  const uint16_t        from;
  const uint16_t        to;
  const FunctionPointer run;
};

Task task[]
{
  {3, 6900, 0, 255,    function0},
  {4, 4600, 256, 511,  [](){Serial.println("function1");}},
  {5, 2500, 512, 767,  [](){Serial.println("function2");}},
  {6, 1000, 787, 1023, [](){Serial.println("function3");}},
};

void setup()
{
  Serial.begin(9600);
  for (auto & i : task) pinMode(i.pinLed, OUTPUT);
  pinMode (pinSwitch, INPUT_PULLUP);
}

void loop()
{
  setSelectionLED();
  checkSwitch();
  conditionalRun();
}

void conditionalRun()
{
  if (active >= 0)
  {
    task[active].run();  // run the active task
    if ((millis() - timestamp >= task[active].functionInterval))  // check for time out
    {
      Serial.print(F("ended function ")); Serial.println(active);
      active = -1;
    }
  }
}

void setSelectionLED()
{
  static uint8_t previousPin = 0;
  uint16_t readingPotentiometer = analogRead(A0);
  for (auto &i : task)
  {
    if (readingPotentiometer >= i.from && readingPotentiometer <= i.to)
      if (i.pinLed != previousPin)
      {
        digitalWrite(i.pinLed, HIGH);
        digitalWrite(previousPin, LOW);
        previousPin = i.pinLed;
        break;
      }
  }
}

void checkSwitch()
{
  static byte lastSwitchState = HIGH;
  byte switchState = digitalRead (pinSwitch);
  if (switchState != lastSwitchState)
  {
    if (switchState == LOW && active ==-1)
    {
      timestamp = millis();
      uint16_t readingPotentiometer = analogRead(A0);
      for (int i = 0; i < sizeof(task) / sizeof(task[0]); i++)
      {
        if (readingPotentiometer >= task[i].from && readingPotentiometer <= task[i].to)
        {
          active = i;
          break;
        }
      }
      Serial.println(F("Start "));
      delay(50); // dirty debounce
    }
    lastSwitchState =  switchState;
  }
}


Und diese Meldungen treten bei mir auf.

E:\Programme\arduino\portable\sketchbook\sketch_jul25d\sketch_jul25d.ino:24:18: warning: 'long' specified with 'uint32_t' {aka 'long unsigned int'} [-Wpedantic]
   24 |   const uint32_t long   functionInterval;
      |                  ^~~~
E:\Programme\arduino\portable\sketchbook\sketch_jul25d\sketch_jul25d.ino: In function 'void conditionalRun()':
E:\Programme\arduino\portable\sketchbook\sketch_jul25d\sketch_jul25d.ino:58:31: warning: comparison of integer expressions of different signedness: 'long unsigned int' and 'const long int' [-Wsign-compare]
   58 |     if ((millis() - timestamp >= task[active].functionInterval))  // check for time out
      |          ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
E:\Programme\arduino\portable\sketchbook\sketch_jul25d\sketch_jul25d.ino: In function 'void checkSwitch()':
E:\Programme\arduino\portable\sketchbook\sketch_jul25d\sketch_jul25d.ino:93:25: warning: comparison of integer expressions of different signedness: 'int' and 'unsigned int' [-Wsign-compare]
   93 |       for (int i = 0; i < sizeof(task) / sizeof(task[0]); i++)
      |                       ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

@noiasca Danke, ich schaue mir structs dann genauer an. Erst möchte ich die von @my_xy_projekt gepostete Version nachvollziehen. Dann kann ich mir mir noch unbekannte Alternativen anschauen. Ich bin ein alter Sack und war nie Programmierer, also immer schön langsam Schritt für Schritt zu neuen Zielen : )

@combie Danke, es ist eigentlich recht schlicht: Mit einem Potentiometer (mit Rastungen, Teil einer alten Drehbank) wähle ich unter vier (später mehr) Funktionen aus. Mit einem Taster starte ich dann die ausgewählte Funktion. Jede Funktion hat eine unterschiedliche Laufzeit (hier nur im Sekundenbereich, später Minuten). Einige Funktionen werden mit einem Motor Schwungräder zum Laufen bringen (wie es sie früher z. B. in Gesenkschmieden gab). Andere Funktionen werden Toneffekte (OGG) über ein externes Audiomodul abspielen. Aber erst einmal möchte ich ein Grundprinzip haben, wie ich Funktionen auswählen und unabhängig voneinander laufen lassen kann. Und das funktioniert schon mal.