Hoe kan ik deze code het beste splitsen in bijvoorbeeld classes

Hoi,

Ik heb deze code :

// Alle pinnen van de leds
const uint8_t ledPins[] = { 9, 11, 10, 6, 3, 5 };

// de pin van de potmeter
byte potPin = A3;
byte switchPin = 12;
// de waarde die de potmeter aangeeft
int potValue;
int switchValue;
bool LED0 = 0;

// geeft de richting aan waar de led moeten draaien.
int richting;

// houdt de index bij van de huidige brandende led
int currentLed = 0;
// houdt de index bij van de leds van de staart
byte trails[5] = {0, 0, 0, 0, 0};

String action;
int oldSwitchValue;


unsigned long previousMillis = 0;
long interval;

void setup() {
  // Zet de seriele monitor aan
  Serial.begin(115200);


  for (uint8_t cnt = 0; cnt < sizeof(ledPins); cnt++) {
    // Zet alle leds uit
    analogWrite(ledPins[cnt], 0);
    // Zet de pin mode op output
    pinMode(ledPins[cnt], OUTPUT);
  }

  pinMode(switchPin, INPUT_PULLUP);
}

void debug(String text, int variable) {
  Serial.print("Mode :");
  Serial.print(action);
  Serial.print('\t');
  Serial.print('\t');
  Serial.print("Led :");
  Serial.print(currentLed);
  Serial.print('\t');
  Serial.print('\t');
  Serial.print(text);
  Serial.print(" : ");
  Serial.print(variable);
  Serial.print('\t');
  Serial.print("Richting");
  Serial.print(" : ");
  Serial.println(richting);

}


void Collect() {
  /**
    Reads the value of the pot meter

    @param values none
    @return void because only a local variable is being changed
  */
  potValue = analogRead(potPin);
  switchValue = digitalRead(switchPin);
}

void reset() {
/**
reset all values to the starting values 
*/
  if (switchValue == 0) {
    currentLed = -1;
  } else {
    currentLed = -2;
  }

 for (int i = 0; i < 5; i++) {
  trails[i] = 0; 
}

  for (uint8_t cnt = 0; cnt < sizeof(ledPins); cnt++) {
    // Zet alle leds uit
    analogWrite(ledPins[cnt], 0);
  }
}



void Process() {
  /**
    Change value of currentled

    Changes the value of some local variables.

    @param values  none
    @return void because only a local variable is being changed
  */


  if (switchValue != oldSwitchValue) {

    
    reset();

    
    oldSwitchValue = switchValue;

    if (switchValue == 0) {
      action = "staart";
      currentLed += richting;
      for (int i = 4; i > 0; i--) {
        trails[i] = trails[i - 1];
      }
    trails[0] = currentLed; 
    }

    if (switchValue == 1) {
      action = "twee_keer";
      currentLed = 0;
      LED0 = !LED0;
    }
  }

   // Set to predefined values
  richting = 0;
  interval = 5000; 

  // 512 - 30 (middle value - offset)
  if (potValue < 482)  
  {
    // richting wordt linksom
    richting = -1;
    // berekenen van de nieuwe interval voor linksom
    interval = map(potValue, 482, 0, 500, 75);  
  }

  // 512 + 30 (middle value + offset)
  if (potValue > 542) {  

    richting = 1;
    // berekenen van de nieuwe interval voor rechtsom
    interval = map(potValue, 1023, 542, 75, 500);  
  }
  debug("switchValue", switchValue);
}

void display_leidend() {
  digitalWrite(ledPins[currentLed], LED0);
}

void display_volgend() {

  if (action == "staart") {
    analogWrite(ledPins[trails[4]], 0);
    analogWrite(ledPins[trails[3]], 25);
    analogWrite(ledPins[trails[2]], 54);
    analogWrite(ledPins[trails[1]], 117);
  }

  if (action == "twee_keer") {
    digitalWrite(ledPins[1], LED0);
    digitalWrite(ledPins[2], !LED0);
    digitalWrite(ledPins[3], !LED0);
    digitalWrite(ledPins[4], LED0);
    digitalWrite(ledPins[5], LED0);
  }
}

void Display() {
  /**
    takes care of de display of leds

    @param values  none
    @return void because nothing changes
  */
  display_volgend();
  display_leidend();
}


void preventOverflow() {
  /**
    take care that there is no overflow

    @param values  none
    @return void because only a local variable is being changed
  */


  if (richting == 1) {
    if (action == "staart") {
      if (currentLed >= sizeof(ledPins) - 1) {
        currentLed = -1;
      }
    }
  }

  if (richting == -1) {
    if (action == "staart") {
      if (currentLed <= 0) {
        currentLed = sizeof(ledPins);
      }
    }
  }
}



bool timer() {

  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    return true;
  }
  return false;
}

void loop() {

  if (timer()) {
    Collect();
    Process();
    Display();
    preventOverflow();
  }
}

Hoe kan ik dit het beste splitsen in classes en misschien wel aparte bestanden zodat het makkelijker is om nieuwe effecten toe te voegen.

JIj gaf me al deze demo.

#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

// function prototypes
void effect1();
void effect2();
void effect3();

// array with function pointers for effects
void (*effects[])() = {
  effect1,
  effect2,
  effect3,
  nullptr,
};

void setup()
{
  Serial.begin(115200);
  Serial.println("function pointer demonstration");
}

void loop()
{
  static uint8_t selected;
  if (Serial.available())
  {
    // read serial data
    char ch = Serial.read();
    // limit 1..9
    if (ch >= '1' && ch <= '9')
    {
      selected = ch - '0';
    }
    // make sure that selected effect does not exceed boundary of effect array
    if (selected > NUMELEMENTS(effects))
    {
      Serial.println(F("Ongeldige keuze"));
      selected = 0;
    }
  }

  // only if a correct selection was made and an effect function was specified in the effect array
  if (selected != 0 && effects[selected - 1] != nullptr)
  {
    // execute selected function
    effects[selected - 1]();
  }
}

void effect1()
{
  Serial.println(__FUNCTION__);
  delay(1000);
}

void effect2()
{
  Serial.println(__FUNCTION__);
  delay(1000);
}

void effect3()
{
  Serial.println(__FUNCTION__);
  delay(1000);
}

Misschien kunnen we die als vertrek punt gebruiken.
En jij hoeft niet alles te veranderen.

Voor anderen die dit willen volgen, het begin van dit topic zit in https://forum.arduino.cc/t/meneer-van-dalen-wacht-op-antwoord-rekenmachine/1386184/286

Ik heb even naar je wokwi gekeken en die lijkt niets te doen (en ja, ik heb de potmeter verdraaid :wink:). Dus moet je uitleggen hoe je meerdere effecten wilt kunnen selecteren en wat die effecten zijn.

Ik heb ook even gekeken, en ik weet niet precies wat er is veranderd.
Maar er gebeurt inderdaad niets meer terwijl het de laatste keer dat ik keek precies deed wat er beschreven was.
De mode schakel je tot nog toe door de switch om te zetten, en je ziet dat ook gebeuren in de serial monitor.
Verder zie je ook dat de reset die we hebben toegevoegd, wordt uitgevoerd doordat de LEDs uitgaan en veranderen naar de modus die dan gekozen is.
Omdat de serial monitor ook updates krijgt, werkt het timing gedeelte ook (en wanneer je aan de knop draait zie je daarom ook dat de timing verandert).
Voor wat ik nu wel kan zien (na iets langer testen) is dat het updaten van de display (de LEDs) niet meer lijkt te gebeuren.
Omdat het debug gedeelte heel inconsistent wordt bijgehouden, mis je daar ook belangrijke informatie die je kan helpen dit soort dingen op te lossen.

Door persoonlijke omstandigheden, ben ik zelf de komende tijd ook niet in staat om hier bij te helpen.

Bedankt, ik had de schakelaar gemist; die doet in ieder geval iets.

Sterkte

Chips. de laatste veranderingen lijken niet opgeslagen te zijn.
Duik daar even in.

Wat we hadden werkend was dit.

Als de schakelaar uit stond, zou je het effect moeten zien dat de leds ronddraaien met een staart erachter.
Als je de potmeter links zetten, dan draaide de potmeter naar links en als je de potmeter naar ongeveer het midden draaide , stond alles still en draaide je naar rechts , dan draaide alles naar rechts.
Hoe verder je de potmeter verder van het midden draaide, werkte het effect sneller.

Als de schakelaar aan stond , dan zie je led1, 2, 5, 6 aan en daarna 3 en 4 aan en dan begint het weer met 1,2,5,6 aan enz.

Derde effect moet ik nog toevoegen.

Hopelijk is dit verhaal duidelijk.
Ik ga nog vandaag of morgen proberen om alles weer werkend te krijgen.

@MAS3 sterkte

Sorry, ben de hele dag bezig om uit te zoeken wat er in de code veranderd is maar ik kan het niet vinden.

Wat mij opvalt is dat de getallen in de seriale monitor ook niet meer de juiste gegevens geven.

Dus mijn idee is dat Process ook niet werkt zoals zou moeten maar zoals ik al gezegd heb, kan ik niet vinden waarom.
En er lijkt dat de code om de twee keer bij overflow opeens weg is.

Check het originele onderwerp.

Alles werkt weer goed zoals ik het kan zien.

Mocht je nog vragen hebben over een van de effecten , gewoon vragen.

Als je verstandig bent zet je je programma ook in je originele "staart" onderwerp zodat je het niet opnieuw kwijt raakt :wink: Altijd handig om een backup te hebben.

Vraagje:
Wat weet je van enums?

Beetje.

IK weet dat je dit kunt doen:

Enum class Acties {
  Staart 
  Twee_keer 
}

En dat je dan alle strings als dit action = "staart" kunt veranderen in action = Acties::staart

En daardoor kan zorgen dat je niet per ongeluk typ-fouten of effecten kunt toevoegen die niet bestaan.

Goed, er is een beetje meer. Ik heb een programma voor je klaar dat je kunt gebruiken als geraamte voor dit onderwerp. Ik heb hiervoor een pure C benadering gekozen en er komt geen OOP in voor (met uitzondering van dat wat gebruikt wordt in de Arduino API). Ik moet de beschrijving nog uit werken en die beschrijving zal op zijn vroegst morgen klaar zijn.

Wat betreft enums, in het nieuwe programma gebruik ik twee enums

enum RUNRESULT
{
  EFFECT_RUNNING,
  EFFECT_COMPLETE,
  EFFECT_ERROR,
};

enum EFFECTDIRECTION
{
  DIR_CCW = -1,
  DIR_NONE,
  DIR_CW
};

Hier is een enum hetzelfde als een int maar met beperkte waardes.
Bijna hetzelfde als wat jij getoond hebt, alleen is het keyword class niet aanwezig.

De eerste enum hierboven heeft de waardes 0 en opeenvolgende namen (enumerators) krijgen opeenvolgende waardes, dus 1 en 2. Je kunt echter ook een waarde toekennen aan de enumerators in de enum zoals getoond in de tweede enum; dat worden de waardes dus -1, 0 en 1. Ik heb dit gebruikt zodat het hetzelfde lijkt als je originele richting variabele.

Je kunt ook alleen EFFECT_ERROR een waarde geven zoals hieronder

enum RUNRESULT
{
  EFFECT_RUNNING,
  EFFECT_COMPLETE,
  EFFECT_ERROR = -1,
};

De waardes van de enumerators zijn dan 0, 1 en -1. Als je een functie hebt die een fout moet kunnen aangeven wordt er vaak een negatieve waarde gebruikt; bv Serial.read() zal -1 teruggeven als er geen data beschikbaar is (merk op dat Serial.read() hier geen enum voor gebruikt, het is een voorbeeld.
Let op dat in dit geval, als je een andere enumerator toevoegt na EFFECT_ERROR, deze de waarde 0 krijgt en dat kan in fouten of onverwacht gedrag van je programma resulteren.

Het voordeel van enums is dat je niet hoeft te onthouden wat de betekenis van de waardes is, de enumerator zou duidelijk genoeg moeten zijn om fouten te voorkomen.

Stel nu dat je twee functies hebt die verschillende enums kunnen teruggeven maar de enums zijn gedeeltelijk hetzelfde.

enum X
{
  OK,
  ERROR,
};

enum Y
{
  OK,
  BUSY,
  ERROR = -1,
};

void setup() {
  Serial.begin(115200);

  Serial.println(ERROR);

}

void loop() {
  // put your main code here, to run repeatedly:

}

Dit zal niet compileren met de volgende foutmeldingen

C:\Users\bugge\AppData\Local\Temp\.arduinoIDE-unsaved202569-6268-11emmhp.ar9q\sketch_jul9a\sketch_jul9a.ino:9:3: error: redeclaration of 'OK'
   OK,
   ^~
C:\Users\bugge\AppData\Local\Temp\.arduinoIDE-unsaved202569-6268-11emmhp.ar9q\sketch_jul9a\sketch_jul9a.ino:3:3: note: previous declaration 'X OK'
   OK,
   ^~
C:\Users\bugge\AppData\Local\Temp\.arduinoIDE-unsaved202569-6268-11emmhp.ar9q\sketch_jul9a\sketch_jul9a.ino:11:12: error: redeclaration of 'ERROR'
   ERROR = -1,
            ^
C:\Users\bugge\AppData\Local\Temp\.arduinoIDE-unsaved202569-6268-11emmhp.ar9q\sketch_jul9a\sketch_jul9a.ino:4:3: note: previous declaration 'X ERROR'
   ERROR,
   ^~~~~
exit status 1

Compilation error: redeclaration of 'OK'

Je moet er dus voor zorgen dat de enumerators uniek zijn.

Jij gaf als voorbeeld een C++ enum. Ze werken hetzelfde als hierboven maar hebben wat voordelen

  1. Je kunt dezelfde namen gebruiken voor de enumerators.
  2. In plaats van int kun je bv int8_t gebruiken hetgeen wat geheugen kan besparen.
enum class X : int8_t
{
  OK,
  ERROR,
};

enum class Y : int8_t
{
  OK,
  BUSY,
  ERROR = -1,
};

void setup() {
  Serial.begin(115200);

  Serial.println((int8_t)X::ERROR);
  Serial.println((int8_t)Y::ERROR);

}

void loop() {
  // put your main code here, to run repeatedly:

}

Nadeel is dat je de class naam moet meegeven en dat je het niet direct kunt printen omdat er geen print functie is die enums begrijpt

En dat is wat ik weet over enums.

Duidelijk

Het is geen probleem dat je C code gebruikt alleen ik ken geen of weinig C .

Ik had het idee dat kennis van C++ genoeg is om met de arduino te werken.
En c++ ben ik nu aan het leren door oefeningen van de exercism.io site te maken.

Misschien beter om ook in C te duiken ?

Niet nodig. C++ is eigenlijk een superset van C. structs zijn puur C maar je kunt er niet direct functies aan toevoegen. Daar zijn wel weer trucjes voor maar in C++ kun je direct functies toevoegen zoals je gezien hebt in de "rekenmachine".

Als je bij Arduino blijft is C++ goed genoeg; als je je gaat verdiepen in de onderliggende functies zul je zien dat een gedeelte nog puur C is (geen classes).

Het is handig om het verschil te weten; niet alles wat in C++ mogelijk is is in C op dezelfde manier mogelijk.

Op dit moment is het idee om bij de Arduino te blijven.

Misschien in de heel verre toekomst als ik meer in robotica ga verdiepen dan zie ik wel weer welke taal dan handig is.

Is het dan de bedoeling dat we hetzelfde gaan doen als bij de "rekenmachine" zo stap voor stap aanpakken ?

Het wordt niet een stap-voor-stap verandering. Ik ga je een raamwerk geven met beschrijving van ieder onderdeel en het enige dat je zelf moet doen is het implementeren van de effecten.

Het is wel gebaseerd op bestaande functionaliteiten in je bestaande programma.

oke

Zolang ik er wat van leer en het uit te breiden is ben ik blij.

@sterretje hoe gaat het met je ?
En een goed weekend gewenst uit Nederland

Nadat je gisteren (in een privé conversatie) hebt aangegeven dat je min of meer niet wilt dat ik het schrijf en uitleg zijn hier wat aanwijzing.

Je kunt beginnen de timer() functie in een apart bestand te zetten (bv timer.cpp).

Wees je er van bewust dat verschillende .cpp bestanden verschillende compilatie eenheden zijn. Dus je zult een foutmelding krijgen op je previousMillis variabele omdat die in het .ino bestand (hetgeen eigenlijk ook een .cpp bestand is) is gedeclareerd en dus niet bekend is in timer.cpp; laat maar horen of je dat kunt oplossen.

Je zult ook in het .ino bestand moeten aangeven dat er ergens een timer() functie is.

Als je dit opgelost hebt kunnen we de volgende stap doen. Als je er niet uitkomt moet je vragen.

krijg het even niet opgelost.

IK krijg deze foutmelding:

C:\Users\rwobb\AppData\Local\arduino\sketches\C7F752F01225498BECD40AED8E608293\sketch\timer.cpp.o (symbol from plugin): In function `timer()':
(.text+0x0): multiple definition of `previousMillis'
C:\Users\rwobb\AppData\Local\arduino\sketches\C7F752F01225498BECD40AED8E608293\sketch\leds_second_effect.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
C:\Users\rwobb\AppData\Local\arduino\sketches\C7F752F01225498BECD40AED8E608293\sketch\timer.cpp.o (symbol from plugin): In function `timer()':
(.text+0x0): multiple definition of `interval'
C:\Users\rwobb\AppData\Local\arduino\sketches\C7F752F01225498BECD40AED8E608293\sketch\leds_second_effect.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
exit status 1

Compilation error: exit status 1

en als ik ga zoeken in de .ino file dan is er helemaal geen vermelding van previousmills

leds_second_effect.zip (2,2 KB)