Goto outside of function or reset arduino using interrupt

Hey guys,

I wrote an extremely long code controlling a Mega which is controlling relais in order to balance LiPo batteries (shortening single cells with power resistors)

In the setup, a menu is displayed on an OLED where different balance modes, accuracies etc. can be selected.

A rotary encoder Is used to control the menu.

The project is almost finished but I now realized it would be really convenient to cancel the balance script
(=loop) and go back to the menu, even better would be to fully reset the Arduino to also reset the variables.

After Setup, I could hook the Button pin to an Interrupt.
In the interrupt function, I’d like to have something like “goto setup” or “reset Arduino”.

Is this doable?

In case you want to see the code, you will understand it is too late to avoid a disreputable “goto solution”, I used delays locking the Arduino instead of timestamps because the program would otherwise be even longer.

void loop() {
  refMessung = messen(0, refVPin); //Referenzmesswert für 4.091V
  
  
  minimalzelle_vorher = minimalzelle;
  // Min und Max aus letztem Durchgang löschen
  minimalzelle = 0;
  maximalzelle = 0;
  
  
  //Min und Max neu bestimmen
  for (int i=0; i<zellenzahl; i++){
    zellen[i][1] = messen(verbinderPins[i], messPin);
  }
  for (int i=0; i<zellenzahl; i++){
    if (zellen[i][1] < zellen[minimalzelle][1]){
      minimalzelle = i;
    }
    if (zellen[i][1] > zellen[maximalzelle][1]){
      maximalzelle = i;
    }
  }
  if (minimalzelle_vorher != -1){ //falls nicht erstser Durchgang
    if (minimalzelle_vorher == minimalzelle){ //falls minimalzelle anders als letzter durchgang, reduziere die Zeit
      minimalzelle_anders = false;
    }
    else{
      minimalzelle_anders = true;
    }
  }
  
  
  //ÄNDERUNG FÜR ENTLADEN
  
  //Abweichung von Min in zellen_sorted Eintrag 3
  
  for (int i=0; i<zellenzahl; i++){
    if (balancemode == 1){ //Balancen
      zellen[i][2] = zellen[i][1]-zellen[minimalzelle][1];
    } else if (balancemode == 2){  //Entladen
      zellen[i][2] = zellen[i][1]-toVoltInv(schlusspannung);
    }
  }
  

  //Array zellen nach zellen_sorted kopieren
  for (i = 0; i < zellenzahl; ++i) 
  {
    for (j=0; j<3; ++j){
      zellen_sorted[i][j] = zellen[i][j];
    }
  }
  
  
  //Array zellen_sorted sortieren
  for (i = 0; i < zellenzahl; ++i) 
  {
    for (j = i + 1; j < zellenzahl; ++j)
    {
      if (zellen_sorted[i][2] > zellen_sorted[j][2]) 
      {

        a =  zellen_sorted[i][2];
        b =  zellen_sorted[i][1];
        c =  zellen_sorted[i][0];
        
        zellen_sorted[i][2] = zellen_sorted[j][2];
        zellen_sorted[i][1] = zellen_sorted[j][1];
        zellen_sorted[i][0] = zellen_sorted[j][0];
        
        zellen_sorted[j][2] = a;
        zellen_sorted[j][1] = b;
        zellen_sorted[j][0] = c;

      }
    }
  }
  
  //Sortierten Array ausgeben
  
  for (i = 0; i < zellenzahl; ++i) 
  {
    Serial.println("Zelle: " + String(zellen_sorted[i][0]+1) + "  Spannung: " + String(toVolt(zellen_sorted[i][1]),4) + "  Abweichung: " + String(toVolt(zellen_sorted[i][2]),4));
  }
  Serial.println("");
  
  
  

  u8g2.setFont(u8g2_font_timB10_tf  );	
  u8g2.clearBuffer();
  u8g2.setDrawColor(1); 
  
  u8g2.drawLine(0,43,128,43);
  u8g2.drawLine(42,23,42,64);
  u8g2.drawLine(85,23,85,64);
  if (balancemode == 1){
    u8g2.drawBox(8, 4, 112, 14);
    u8g2.setDrawColor(2);
    u8g2.setCursor(11, 16);
    u8g2.drawUTF8(69,16,"±");
    u8g2.print("Balancen    "+String(1.0*balanceacc,1)+"mV");
  } else if (balancemode == 2){
    if (displaycurrentmode){
      u8g2.drawBox(32, 4, 63, 14);
      u8g2.setDrawColor(2);
      u8g2.setCursor(36, 16);
      u8g2.print("Balancen");
    } else {
      u8g2.drawBox(15, 4, 97, 14);
      u8g2.setDrawColor(2);
      u8g2.setCursor(17, 16);
      u8g2.drawUTF8(60,16,"±");
      u8g2.print(String(schlusspannung,3)+"V    "+String(1.0*balanceacc,1)+"mV");
    }
  }

  u8g2.setFont(u8g2_font_helvB08_tf );	
  
  
  
  for (int k=0; k < zellenzahl; k++){
    if (k < 3){
      u8g2.setCursor(9+(2-k)*43, 53);
      u8g2.print(toVolt(zellen[k][1]),4);
      
      if (zellen[k][2] != 0){
        u8g2.setCursor(2+(2-k)*43, 63);
        u8g2.print("+");
        u8g2.setCursor(9+(2-k)*43, 63);
        u8g2.print(toVolt(zellen[k][2]),4);
      } else{
        u8g2.setDrawColor(2); 
        u8g2.drawBox((2-k)*43, 44, 42, 20);
        u8g2.setDrawColor(1); 
      }
    } else {
      u8g2.setCursor(9+(k-3)*43, 32);
      u8g2.print(toVolt(zellen[k][1]),4);
      
      if (zellen[k][2] != 0){
        u8g2.setCursor(2+(k-3)*43, 42);
        u8g2.print("+");
        u8g2.setCursor(9+(k-3)*43, 42);
        u8g2.print(toVolt(zellen[k][2]),4);
      } else{
        u8g2.setDrawColor(2); 
        u8g2.drawBox((k-3)*43, 23, 42, 20);
        u8g2.setDrawColor(1); 
      }
    }
  }
  u8g2.updateDisplay();
  displaycurrentmode = not displaycurrentmode;
  
  
  
  //Balancen oder Entladen
  
  
  
  
double zeitscalefaktor = 1.0;
  
  
  if (zellen_sorted[zellenzahl-1][2] > 450){  //Grobes Balancen für Abweichung > 5mV
    Serial.println("Grobes Balancen");

    if (minimalzelle_anders){ //falls minimalzelle anders als letzter durchgang, reduziere die Zeit
      zellenschutzfaktor=zellenschutzfaktor*2/3;
      Serial.print("Minimalzelle anders, neuer Zeitfaktor ");
      Serial.println(zellenschutzfaktor);
    }
    
    if (zellen_sorted[zellenzahl-1][2] >= 1000){  //Schutz: Zelle nicht länger als 30s anklemmen
      zeitscalefaktor = (double(1000)/zellen_sorted[zellenzahl-1][2]);  //Darum werden alle Dauern dann mit einem Skalenfaktor skaliert, sodass die längste Zelle 30s lang angeklemmt ist.
      Serial.println(zellen_sorted[zellenzahl-1][2]);
      Serial.println("zeitscalefaktor" + String(zeitscalefaktor));
    }
    
    for (i = 1; i < zellenzahl; ++i) {
      if (zellen_sorted[i][2] > 450) digitalWrite(verbraucherPins[zellen_sorted[i][0]], HIGH);
    }
    for (i = 1; i < zellenzahl; ++i) {
      delay((zellen_sorted[i][2]-zellen_sorted[i-1][2])*double(40)*zeitscalefaktor*zellenschutzfaktor);  //Skalenfaktor als Verhinderung für mehr als 40s.
      Serial.println((zellen_sorted[i][2]-zellen_sorted[i-1][2])*double(40)*zeitscalefaktor);
      digitalWrite(verbraucherPins[zellen_sorted[i][0]], LOW);
    }  
    delay(2000);
    fertigcounter = 5;
  }
  
  else if (zellen_sorted[zellenzahl-1][2] > 90){//Mittleres Balancen für Abweichung > 1mV
    Serial.println("Mittleres Balancen");
    
    if (minimalzelle_anders){
      zellenschutzfaktor=zellenschutzfaktor*2/3;
      Serial.print("Minimalzelle anders, neuer Zeitfaktor ");
      Serial.println(zellenschutzfaktor);
    }
    
    for (i = 1; i < zellenzahl; ++i) {
      if (zellen_sorted[i][2] > 90) digitalWrite(verbraucherPins[zellen_sorted[i][0]], HIGH);
    }
    for (i = 1; i < zellenzahl; ++i) {
      delay((zellen_sorted[i][2]-zellen_sorted[i-1][2])*24*zellenschutzfaktor);
      digitalWrite(verbraucherPins[zellen_sorted[i][0]], LOW);
    }  
    delay(2000);
    fertigcounter = 5;
  }
  
  else if (zellen_sorted[zellenzahl-1][2] >= balanceacc*90){//Feines Balancen für Abweichung >= Balanceacc 90=1mV
    Serial.println("Feines Balancen");
    
    for (i = 1; i < zellenzahl; ++i) {
      if (zellen_sorted[i][2] >= balanceacc*90) digitalWrite(verbraucherPins[zellen_sorted[i][0]], HIGH);
    }
    for (i = 1; i < zellenzahl; ++i) {
      
      delay((zellen_sorted[i][2]-zellen_sorted[i-1][2])*10*zellenschutzfaktor);  //Hier vielleicht: Wenn balanceacc weniger gleich als 0.4, dann kürzeres delay
      if (balanceacc > 0.4){delay((zellen_sorted[i][2]-zellen_sorted[i-1][2])*10*zellenschutzfaktor);};
      digitalWrite(verbraucherPins[zellen_sorted[i][0]], LOW);
    }  
    delay(4000);  //innerhalb balanceacc: länger messen
    fertigcounter = 5;
  }
  
  //haben alle Zellen eine Abweichung von weniger als balanceacc, dann reduziere den fertigcounter um 1.
  else{
    Serial.println("Noch " + String(--fertigcounter) + " bis fertig");
    delay(3000);
  }

  //ist der fertigcounter bei 0, beende das Programm, spiele Ton ab. Wird der Akku entfernt, beende den Ton.
  if (fertigcounter == 0){
    while (true){
      lied(melodie_comptine, dauern_comptine, 0);
      if (messen(verbinderPins[0], messPin) <= 1000){
        while (true){}
      }
    }
  }
}

There is no shame in using what you have learned and completely rewrite your project from the beginning.

Paul

It's not really difficult. You just make a custom loop() for example taskLoop() and taskSetup() and call them from the button logic. The actual setup() would be empty except for button switch setup. Whenever you get a button switch press, you run taskSetup(). taskLoop would be placed in loop(), together with the button logic.

In every other respect, taskSetup and taskLoop would function exactly the same as setup() and loop() except for a tiny call overhead (which might even be optimized away).

It’s not so hard to restructure. But your main problem will be the delays and you cannot be listening for a button press while you are waiting in a delay. So your application will/May not be responsive depending on lengths of the delays.

Unfortunately you cannot keep applying “sticking plaster” to programs to work around initial design decisions which are no longer correct. Sooner or later you will need to rewrite.

The only way to programmatically “reset” an Arduino, without external hardware, is to abuse a watchdog timeout. You could call setup() from inside loop() if you want when you detect the button press. But you are still going to have a responsiveness issue if you are inside a delay when the button is pressed.

In case you want to see the code, you will understand it is too late to avoid a disreputable “goto solution”, I used delays

it’s never to late to write proper code! If I have a look on your recent posts it seems you are working on some kind of battery balancer since December.

Well, I guess you can start to write not blocking code today and you will have a nice solutions within days. Not weeks, not months.

In other words: get rid of your delay() and while (true){}

I agree it is never too late to write proper code.
But as someone mentioned, I am writing this since before December, and have learned a lot so far. This special code has 800 lines and 23.369 chars.

I reworked it partially but do not fully understand every code section anymore. Of course, I could get to know it again, but this would take hours, hours I do not have.

I started many projects since then and learned a lot in the process. My latest uses an array of timestamps+delays and then checks for them to be reached in the loop. This does not but that is also its charm.

One of my first programs, Definitely the biggest one so far and I want it to stay it this way so I can look back in a few years and see from where I have come.

Fully reworking it would be as releasing your first kid for adoption because it is not beautiful enough, even though it has a purpose and gets stuff done.

aarg:
You just make a custom loop() for example taskLoop() and taskSetup() and call them from the button logic.

Will defenitely take a look at that, thank you!

aarg:
It's not really difficult. You just make a custom loop() for example taskLoop() and taskSetup() and call them from the button logic. The actual setup() would be empty except for button switch setup. Whenever you get a button switch press, you run taskSetup(). taskLoop would be placed in loop(), together with the button logic.

I tried but did not get it working. As soon as I call setupmenu() in the interrupt function, the program Manages to display BU (from "BUTTON") and then gets stuck.

Button presses are registered flawlessly until stuck, also during delays.

Here the (Pseudo)Code:

void setup() {
 attachInterrupt(digitalPinToInterrupt(inputSW), buttonPressed, RISING);
 setupmenu();
}

void setupmenu() {
 WHOLE MENU, button used as "enter"
}

voild loop(){
 measureloop();
}

void measureloop(){
 WHOLE LOOP
}

void buttonPressed(){
 Serial.println("BUTTON");
 if (boolean in_loop){
  setupmenu();
 }
}

This may work:

Use the microprocessor “reset” function.

You could put a pushbutton in parallel with the reset pushbutton on the Arduino.

a7

alto777:
This may work:

Use the microprocessor "reset" function.

You could put a pushbutton in parallel with the reset pushbutton on the Arduino.

a7

Yes, no joke. You can also connect a spare GPIO to the RESET pin, enable it as an output and pull it LOW when you want to reset the processor, if you want to do it under software control and don't want to mess with the watchdog.

All ugly hacks. Cut your losses, put that ugly kid up for adoption and do it right.

This request comes up from time to time. It's generally misguided - almost always an attempt to compensate for problems deriving from a lack of top down design.

gfvalvo:
All ugly hacks. Cut your losses, put that ugly kid up for adoption and do it right.

I have like 60 years to live. I can think of more beautiful things to do in my spare time than completely rewriting a code I wrote months ago when I did not program properly.

If i can save like 5h of my liftime by a simple

void interruptFunction(){digitalWrite(5h-lifetimesaving-Pin, LOW);}

and two solder points, I will happily choose this option.
You may not, but this is my decision.
I did not create this thread for policy discussions, neither said things would be colourful and shiny. Sometimes it gets dirty. This is life.

If you desperately need beautiful code, feel free to rewrite my NEW loop with timestamps:

if (zellen_sorted[zellenzahl-1][2] > 270){  //Grobes Balancen für Abweichung > 5mV
    Serial.println("Grobes Balancen");
    zellenschutzfaktor = 1.0;

    if (zellen_sorted[zellenzahl-1][2] >= 1000){  //Schutz: Zelle nicht länger als Ns anklemmen. 1000ms ist maximalwert durch zelle*zeitscalefaktor
      zeitscalefaktor = (float(1000)/float(zellen_sorted[zellenzahl-1][2]));  //Darum werden alle Dauern dann mit einem Skalenfaktor skaliert, sodass die längste Zelle 30s lang angeklemmt ist.
      Serial.println("Zeitscalefaktor " + String(zeitscalefaktor));
    }

    for (i = 1; i < zellenzahl; ++i) {
      if (zellen_sorted[i][2] > 270) digitalWrite(verbraucherPins[zellen_sorted[i][0]], HIGH);
    }
    if (balancemode == 2){
      if (zellen_sorted[0][2] > 270) digitalWrite(verbraucherPins[zellen_sorted[0][0]], HIGH); //für entladen stecke auch die niedrigste Zelle an
      balancegewicht = ((zellen_sorted[zellenzahl-1][2]-zellen_sorted[0][2])/float(zellen_sorted[0][2]))*3;
      Serial.println("Balancegewicht " + String(balancegewicht));
      zusatzdelay = zellen_sorted[0][2]*zeitscalefaktor*40*zellenschutzfaktor*(1/balancegewicht);
      gesamtdelay = zusatzdelay;
      delay(zusatzdelay); //für entladen zustazdelay zwischen minimalzelle und sollwert
      digitalWrite(verbraucherPins[zellen_sorted[0][0]], LOW);
      Serial.println("Zelle: " + String(zellen_sorted[0][0]+1) + "  Spannung: " + String(toVolt(zellen_sorted[0][1]),4) + "  Abweichung: " + String(toVolt(zellen_sorted[0][2]),4)+ "  Dauer: " + String(zusatzdelay/float(1000)));
    }
    for (i = 1; i < zellenzahl; ++i) {
      grob_delay = (zellen_sorted[i][2]-zellen_sorted[i-1][2])*zeitscalefaktor*40*zellenschutzfaktor*balancegewicht; //Skalenfaktor als Verhinderung für mehr als 40s.
      gesamtdelay += grob_delay;
      delay(grob_delay);
      digitalWrite(verbraucherPins[zellen_sorted[i][0]], LOW);
      Serial.println("Zelle: " + String(zellen_sorted[i][0]+1) + "  Spannung: " + String(toVolt(zellen_sorted[i][1]),4) + "  Abweichung: " + String(toVolt(zellen_sorted[i][2]),4)+ "  Dauer: +" + String(grob_delay/float(1000)));
    }
    Serial.println("                                        Gesamte Dauer: " + String(gesamtdelay/float(1000)));
    delay(5000);
    fertigcounter = 5;
  }

  else if (zellen_sorted[zellenzahl-1][2] > 90){//Mittleres Balancen für Abweichung > 1mV
    Serial.println("Mittleres Balancen");

    if (minimalzelle_anders){ //falls minimalzelle anders als letzter durchgang, reduziere die Zeit
      if ((zellen_sorted[zellenzahl-1][2]-zellen_sorted[0][2])>45){ //reduziere nur, wenn abweichung zw erster und letzter zelle größer als 0.5mV
        zellenschutzfaktor=zellenschutzfaktor*2/3;
        Serial.print("Minimalzelle anders, neuer Zellenschutzfaktor ");
        Serial.println(zellenschutzfaktor);
      }
    }

    for (i = 1; i < zellenzahl; ++i) {
      if (zellen_sorted[i][2] > 90) digitalWrite(verbraucherPins[zellen_sorted[i][0]], HIGH);
    }
    if (balancemode == 2){
      if (zellen_sorted[0][2] > 90) digitalWrite(verbraucherPins[zellen_sorted[0][0]], HIGH); //für entladen stecke auch die niedrigste Zelle an
      balancegewicht = ((zellen_sorted[zellenzahl-1][2]-zellen_sorted[0][2])/float(zellen_sorted[0][2]))*3;
      Serial.println("Balancegewicht " + String(balancegewicht));
      zusatzdelay = zellen_sorted[0][2]*30*zellenschutzfaktor*(1/balancegewicht);
      gesamtdelay = zusatzdelay;
      delay(zusatzdelay); //für entladen zustazdelay zwischen minimalzelle und sollwert
      digitalWrite(verbraucherPins[zellen_sorted[0][0]], LOW);
      Serial.println("Zelle: " + String(zellen_sorted[0][0]+1) + "  Spannung: " + String(toVolt(zellen_sorted[0][1]),4) + "  Abweichung: " + String(toVolt(zellen_sorted[0][2]),4)+ "  Dauer: " + String(zusatzdelay/float(1000)));
    }
    for (i = 1; i < zellenzahl; ++i) {
      grob_delay = (zellen_sorted[i][2]-zellen_sorted[i-1][2])*30*zellenschutzfaktor*balancegewicht;
      gesamtdelay += grob_delay;
      delay(grob_delay);
      digitalWrite(verbraucherPins[zellen_sorted[i][0]], LOW);
      Serial.println("Zelle: " + String(zellen_sorted[i][0]+1) + "  Spannung: " + String(toVolt(zellen_sorted[i][1]),4) + "  Abweichung: " + String(toVolt(zellen_sorted[i][2]),4)+ "  Dauer: +" + String(grob_delay/float(1000)));
    }
    Serial.println("                                        Gesamte Dauer: " + String(gesamtdelay/float(1000)));
    delay(5000);
    fertigcounter = 5;
  }

  else if (zellen_sorted[zellenzahl-1][2] >= balanceacc*90){//Feines Balancen für Abweichung >= Balanceacc 90=1mV
    Serial.println("Feines Balancen");

      for (i = 1; i < zellenzahl; ++i) {
        if (zellen_sorted[i][2] >= balanceacc*90) digitalWrite(verbraucherPins[zellen_sorted[i][0]], HIGH);
      }
      if (balancemode == 2){
        if (zellen_sorted[0][2] >= balanceacc*90) digitalWrite(verbraucherPins[zellen_sorted[0][0]], HIGH); //für entladen stecke auch die niedrigste Zelle an
        balancegewicht = ((zellen_sorted[zellenzahl-1][2]-zellen_sorted[0][2])/float(zellen_sorted[0][2]))*3;
        Serial.println("Balancegewicht " + String(balancegewicht));
        zusatzdelay = zellen_sorted[0][2]*30*zellenschutzfaktor*(1/balancegewicht);
        if (balanceacc < 1)  zusatzdelay *= balanceacc;
        gesamtdelay = zusatzdelay;
        delay(zusatzdelay); //für entladen zustazdelay zwischen minimalzelle und sollwert
        digitalWrite(verbraucherPins[zellen_sorted[0][0]], LOW);
        Serial.println("Zelle: " + String(zellen_sorted[0][0]+1) + "  Spannung: " + String(toVolt(zellen_sorted[0][1]),4) + "  Abweichung: " + String(toVolt(zellen_sorted[0][2]),4)+ "  Dauer: " + String(zusatzdelay/float(1000)));
      }
      for (i = 1; i < zellenzahl; ++i) {
        grob_delay = (zellen_sorted[i][2]-zellen_sorted[i-1][2])*30*zellenschutzfaktor*balancegewicht;
        if (balanceacc < 1)  grob_delay *= balanceacc;
        gesamtdelay += grob_delay;
        delay(grob_delay);
        digitalWrite(verbraucherPins[zellen_sorted[i][0]], LOW);
        Serial.println("Zelle: " + String(zellen_sorted[i][0]+1) + "  Spannung: " + String(toVolt(zellen_sorted[i][1]),4) + "  Abweichung: " + String(toVolt(zellen_sorted[i][2]),4)+ "  Dauer: +" + String(grob_delay/float(1000)));
      }
      Serial.println("                                        Gesamte Dauer: " + String(gesamtdelay/float(1000)));
      delay(5000); //innerhalb balanceacc: länger messen
      fertigcounter = 5;
    }

  //haben alle Zellen eine Abweichung von weniger als balanceacc, dann reduziere den fertigcounter um 1.
  else{
    Serial.println("Noch " + String(--fertigcounter) + " bis fertig");
    delay(3000);
  }
  Serial.println("____________________________________________________________\n");

  //ist der fertigcounter bei 0, beende das Programm, spiele Ton ab. Wird der Akku entfernt, beende den Ton.
  if (fertigcounter == 0){
    while (true){
      lied(melodie_comptine, dauern_comptine, 0);
      if (messen(verbinderPins[0], messPin) <= 1000){
        while (true){}
      }
    }
  }

delay() is used 41 times in this segment. HAVE FUN.

aarg:
Yes, no joke. You can also connect a spare GPIO to the RESET pin, enable it as an output and pull it LOW when you want to reset the processor, if you want to do it under software control and don’t want to mess with the watchdog.

Thanks again. I will probably go for the WDT.
Anyway, could you tell me what I misunderstood from your last post (my last post)?