Guru Meditation Error: Core 0 panic'ed (IllegalInstruction). Exception was unhandled

Hello,

I am very new to coding and I have rewritten code I made with chatGPT, because it didnt do what I wanted. I am using an ESP32-S3 to controll a reeffilter (rollermat). It;s connected to 3 buttons, 1 switch, 3 LEDs and an ISP-display 240x320. I wanted to show realtime info on the display of states (buttons and switch) and variables. So i thought it would be best to split it over 2 cores. Now it gets tricky and I cant get it to work.

14:55:09.251 -> Guru Meditation Error: Core  0 panic'ed (IllegalInstruction). Exception was unhandled.
14:55:09.251 -> Memory dump at 0x420033d8: fff125ff 00000090 21004136
14:55:09.251 -> Core  0 register dump:
14:55:09.251 -> PC      : 0x420033dc  PS      : 0x00060c30  A0      : 0x00000000  A1      : 0x3fca6ed0  
14:55:09.251 -> A2      : 0x00000000  A3      : 0x00000000  A4      : 0x00000000  A5      : 0x00000000  
14:55:09.251 -> A6      : 0x00000000  A7      : 0x00000000  A8      : 0x820033dc  A9      : 0x3fca6eb0  
14:55:09.251 -> A10     : 0x3fc99c00  A11     : 0x00000000  A12     : 0x00000001  A13     : 0x00001ecd  
14:55:09.251 -> A14     : 0x80000001  A15     : 0xb33fffff  SAR     : 0x00000017  EXCCAUSE: 0x00000000  
14:55:09.251 -> EXCVADDR: 0x00000000  LBEG    : 0x00000000  LEND    : 0x00000000  LCOUNT  : 0x00000000  
14:55:09.251 -> 
14:55:09.251 -> 
14:55:09.251 -> Backtrace: 0x420033d9:0x3fca6ed0
14:55:09.251 -> 
14:55:09.251 -> 
14:55:09.252 -> 
14:55:09.252 -> 
14:55:09.252 -> ELF file SHA256: 0f2fc2409cf4dd56
14:55:09.252 -> 
14:55:09.252 -> Rebooting...
14:55:09.252 -> ESP-ROM:esp32s3-20210327
14:55:09.252 -> Build:Mar 27 2021
14:55:09.294 -> rst:0xc (RTC_SW_CPU_RST),boot:0x8 (SPI_FAST_FLASH_BOOT)
14:55:09.294 -> Saved PC:0x403778ed
14:55:09.294 -> SPIWP:0xee
14:55:09.294 -> mode:DIO, clock div:1
14:55:09.294 -> load:0x3fce3818,len:0x508
14:55:09.294 -> load:0x403c9700,len:0x4
14:55:09.294 -> load:0x403c9704,len:0xad0
14:55:09.294 -> load:0x403cc700,len:0x29e4
14:55:09.294 -> entry 0x403c9880

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <WiFi.h>
#include <time.h>
#include <Preferences.h>
#include <Adafruit_NeoPixel.h>

// Pin definitions
#define LED_PIN 48  // Builtin LED
#define NUM_LEDS 1
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

#define FLOAT_SWITCH_PIN 4
#define RESET_BUTTON_PIN 41
#define CW_BUTTON_PIN 42
#define CCW_BUTTON_PIN 40
#define CW_MOTOR_PIN 15
#define CCW_MOTOR_PIN 16
#define TFT_CS 9
#define TFT_RST 46
#define TFT_DC 3
#define TFT_MOSI 8
#define TFT_SCK 18
#define TFT_LED 17
#define GREEN_LED_PIN 39
#define YELLOW_LED_PIN 38
#define RED_LED_PIN 37

//Setup definities
const char *ssid = "X";
const char *password = "X";
const char *ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 3600;      // UTC +1 (CET)
const int daylightOffset_sec = 3600;  // 1 hour for daylight saving time (CEST)
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCK, TFT_RST);
Preferences preferences;

//Variabelen
unsigned long Tijd_rotatie = 10000;
float Afst_rotatie = 50;  //cm
unsigned long Float_tijd = 10000;
unsigned long Reset_knop = 5000;
float rotatieduur = 10000;
float Rolverbruik = 0;
float Rollengte = 2500;
float Totaal_rotaties = 0;
unsigned long Laatste_rotatie = 0;
unsigned long Totaal_rotatietijd = 0;
unsigned long Gem_rotatietijd = 0;
bool displayUpdate = true;
int seconds_left = 0;
int start_tijd = 0;
int duur = 0;
int verbruik = 0;
unsigned long last_rotation_time = 0; 

void (*resetFunc)(void) = 0;

void connectWiFi() {
  WiFi.begin(ssid, password);
  int attempts = 0;
  int max_attempts = 10;  // Maximum number of attempts before giving up
  bool connected = false;

  while (attempts < max_attempts && !connected) {
    delay(5000);  // Wait 5 seconds before checking connection status
    attempts++;

    if (WiFi.status() == WL_CONNECTED) {
      connected = true;
    }
  }

  if (connected) {
    uint8_t brightness = 100;
    strip.setBrightness(brightness);
    strip.setPixelColor(0, strip.Color(0, 0, 255));  // Blue
    strip.show();
    delay(1000);
  } else {
    uint8_t brightness = 255;
    strip.setBrightness(brightness);
    strip.setPixelColor(0, strip.Color(255, 0, 0));  // Red
    strip.show();
    delay(5000);
    // Here you could implement a fallback mechanism or retry logic if needed
  }
}
void syncTime() {
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  struct tm timeinfo;
  if (getLocalTime(&timeinfo)) {
    uint8_t brightness = 100;
    strip.setBrightness(brightness);
    strip.setPixelColor(0, strip.Color(0, 255, 0));  // Green
    strip.show();
    delay(1000);  // Turn on yellow LED if time synchronized
  } else {
    uint8_t brightness = 255;
    strip.setBrightness(brightness);
    strip.setPixelColor(0, strip.Color(255, 255, 0));  // Yellow
    strip.show();
    delay(5000);
  }
}
String getTime() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    return "N/A";
  }
  char buffer[10];
  strftime(buffer, sizeof(buffer), "%H:%M:%S", &timeinfo);
  return String(buffer);
}
String getDate() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    return "N/A";
  }
  char buffer[12];
  strftime(buffer, sizeof(buffer), "%d-%m-%Y", &timeinfo);
  return String(buffer);
}
// Function to split duration into days, hours, minutes, and seconds
void formatDuration(unsigned long duration, int &days, int &hours, int &minutes, int &seconds) {
  days = duration / 86400;
  duration %= 86400;
  hours = duration / 3600;
  duration %= 3600;
  minutes = duration / 60;
  seconds = duration % 60;
}
void displayBasis() {
  static unsigned long float_schakel_tijd2 = 0;                   // Ensure this is static to maintain value between function calls
  bool floatSwitchState2 = digitalRead(FLOAT_SWITCH_PIN) == LOW;  // Reading the state of the float switch
  unsigned long current_time2 = millis();                         // Get the current time

  if (displayUpdate == true) {
    tft.fillScreen(ST77XX_BLACK);
    displayUpdate = false;
  } else {
    tft.setCursor(10, 10);
    tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
    tft.setTextSize(2);
    tft.print(getTime());
    tft.setCursor(150, 10);
    tft.print(getDate());

    tft.setTextSize(1);
    tft.setCursor(30, 10);
    tft.print("Verbruik rol: ");
    tft.setCursor(30, 150);
    tft.print(Rolverbruik);

    tft.setCursor(50, 10);
    tft.print("Aantal rotaties: ");
    tft.setCursor(50, 150);
    tft.print(Totaal_rotaties);

    unsigned long current_time = millis();
    int days, hours, minutes, seconds;
    // Duration since last rotation
    unsigned long duration_since_last_rotation = (current_time - last_rotation_time) / 1000;
    formatDuration(duration_since_last_rotation, days, hours, minutes, seconds);
    tft.setCursor(70, 10);
    tft.print("Sinds laatste rotatie: ");
    tft.setCursor(70, 150);
    tft.printf("%02d:%02d:%02d\n", days, hours, minutes, seconds); 

    tft.setCursor(90, 10);
    tft.print("Gem. tijd rotatie: ");
    tft.setCursor(90, 150);
    int days1, hours1, minutes1, seconds1;
    // Duration since last rotation
    unsigned long Gem_rotatietijd = Totaal_rotatietijd / Totaal_rotaties;
    formatDuration(Gem_rotatietijd, days1, hours1, minutes1, seconds1);
    tft.setCursor(70, 10);
  }    
}
void displayReset() {
  if (displayUpdate == true) {
    tft.fillScreen(ST77XX_BLACK);
    displayUpdate = false;
  } else {
    tft.setCursor(150, 100);
    tft.setTextColor(ST77XX_RED, ST77XX_BLACK);
    tft.setTextSize(2);
    tft.print(seconds_left);
  }
  if (seconds_left <= 0) {
    unsigned long reset_complete_time = millis() + 5000;
    while (millis() < reset_complete_time) {
  // Do nothing, wait for 5 seconds
    };
    tft.fillScreen(ST77XX_BLACK);
    displayBasis();
  }
}
void startMotorKlok() {
  digitalWrite(CW_MOTOR_PIN, HIGH);
  digitalWrite(CCW_MOTOR_PIN, LOW);
}
void startMotorTegen() {
  digitalWrite(CW_MOTOR_PIN, LOW);
  digitalWrite(CCW_MOTOR_PIN, HIGH);
}
void stopMotor() {
  digitalWrite(CW_MOTOR_PIN, LOW);
  digitalWrite(CCW_MOTOR_PIN, LOW);
}
void motorKnoppen() {
  bool cwButtonState = digitalRead(CW_BUTTON_PIN) == LOW; // Controleer of CW knop ingedrukt is
  bool ccwButtonState = digitalRead(CCW_BUTTON_PIN) == LOW; // Controleer of CCW knop ingedrukt is
  unsigned long current_time = millis(); // Huidige tijd verkrijgen

  if (cwButtonState && ccwButtonState) {
    // Beide knoppen tegelijkertijd ingedrukt
    tft.fillScreen(ST77XX_BLACK);
    tft.setCursor(10, 100);
    tft.setTextColor(ST77XX_RED);
    tft.setTextSize(3);
    tft.print("REBOOT");
    displayUpdate = true;
    delay(5000);
    resetFunc(); // Reset Arduino (vereist een functie resetFunc() die elders gedefinieerd is)
  } else if (cwButtonState) {
    // Alleen CW knop ingedrukt
    startMotorKlok(); // Start de motor met de klok mee
    start_tijd = current_time; // Tijd bijhouden wanneer motor is gestart
    duur = current_time - start_tijd; // Bereken de verstreken tijd
    verbruik = Rollengte * (duur / rotatieduur); // Bereken verbruik
    Rolverbruik += verbruik; // Update totaal verbruik
    Totaal_rotaties += (verbruik / Afst_rotatie);
  } else if (ccwButtonState) {
    // Alleen CCW knop ingedrukt
    startMotorTegen(); // Start de motor tegen de klok in
    start_tijd = current_time; // Tijd bijhouden wanneer motor is gestart
    duur = current_time - start_tijd; // Bereken de verstreken tijd
    verbruik = Rollengte * (duur / rotatieduur); // Bereken verbruik
    Rolverbruik -= verbruik;// Update totaal verbruik (aftrekken bij tegen de klok in)
    Totaal_rotaties -= (verbruik / Afst_rotatie); 
  } else {
    // Geen knoppen ingedrukt
    stopMotor(); // Stop de motor
    duur = 0; // Reset de tijd
  }
}
void floatSwitch() {
  static unsigned long float_schakel_tijd = 0;                   // Ensure this is static to maintain value between function calls
  bool floatSwitchState = digitalRead(FLOAT_SWITCH_PIN) == LOW;  // Reading the state of the float switch
  unsigned long current_time = millis();                         // Get the current time

  if (floatSwitchState) {  // If the float switch is LOW
    if (float_schakel_tijd == 0) {
      float_schakel_tijd = current_time;  // Set the switch time
    } else if (current_time - float_schakel_tijd >= Float_tijd) {
      startMotorKlok();        // Start the motor
      delay(rotatieduur);      // Wait for the rotation duration
      stopMotor();             // Stop the motor
      float_schakel_tijd = 0;  // Reset the switch time
      Totaal_rotaties ++;
      Rolverbruik += Afst_rotatie;
      Totaal_rotatietijd += (current_time - last_rotation_time);
      last_rotation_time = current_time; 
      }
  } else {                   // If the float switch is HIGH
    float_schakel_tijd = 0;  // Reset the switch time
  }
}
void resetKnop() {
  static unsigned long reset_druk_tijd = 0;
  bool ResetButtonState = digitalRead(RESET_BUTTON_PIN) == LOW;
  unsigned long current_time = millis();

  if (ResetButtonState) {
    if (reset_druk_tijd == 0) {
      reset_druk_tijd = current_time;
      displayUpdate = true;
      displayReset();
    } else {
      seconds_left = (Reset_knop - (current_time - reset_druk_tijd)) / 1000;  // Update the existing seconds_left variable

      if (current_time - reset_druk_tijd >= Reset_knop) {
        Rolverbruik = 0;
        Totaal_rotaties = 0;
        Laatste_rotatie = 0;
        Totaal_rotatietijd = 0;
        preferences.putFloat("Rolverbruik", Rolverbruik);
        preferences.putUInt("Totaal_rotaties", Totaal_rotaties);
        preferences.putULong("Laatste_rotatie", Laatste_rotatie);
        preferences.putULong("Totaal_rotatietijd", Totaal_rotatietijd);
        tft.fillScreen(ST77XX_BLACK);
        tft.setCursor(10, 100);
        tft.setTextColor(ST77XX_RED);
        tft.setTextSize(2);
        tft.print("RESET COMPLETE");
        delay(5000);
        resetFunc();
      }
    }
  } else {
    displayUpdate = true;
    displayBasis();
    reset_druk_tijd = 0;
  }
}
void LEDS() {
  if (Rolverbruik < 0.75 * Rollengte) {
    digitalWrite(GREEN_LED_PIN, HIGH);
    digitalWrite(YELLOW_LED_PIN, LOW);
    digitalWrite(RED_LED_PIN, LOW);
  } else if (Rolverbruik < 0.9 * Rollengte) {
    digitalWrite(GREEN_LED_PIN, LOW);
    digitalWrite(YELLOW_LED_PIN, HIGH);
    digitalWrite(RED_LED_PIN, LOW);
  } else {
    digitalWrite(GREEN_LED_PIN, LOW);
    digitalWrite(YELLOW_LED_PIN, LOW);
    digitalWrite(RED_LED_PIN, HIGH);
  }
}
void handleLogic(void* parameter) {
  motorKnoppen();
  floatSwitch();
  resetKnop();
}
void handleDisplay(void* parameter) {
  displayBasis();
  displayReset();
  LEDS();
}
void setup() {
  Serial.begin(115200);
  pinMode(FLOAT_SWITCH_PIN, INPUT_PULLUP);
  pinMode(RESET_BUTTON_PIN, INPUT_PULLUP);
  pinMode(CW_BUTTON_PIN, INPUT_PULLUP);
  pinMode(CCW_BUTTON_PIN, INPUT_PULLUP);
  pinMode(CW_MOTOR_PIN, OUTPUT);
  pinMode(CCW_MOTOR_PIN, OUTPUT);
  pinMode(GREEN_LED_PIN, OUTPUT);
  pinMode(YELLOW_LED_PIN, OUTPUT);
  pinMode(RED_LED_PIN, OUTPUT);
  digitalWrite(TFT_LED, HIGH);

  strip.begin();
  strip.show();  // Initialize all pixels to 'off'

  tft.init(240, 320);
  tft.invertDisplay(0);
  tft.setRotation(1);

  preferences.begin("my-app", false);
  Rolverbruik = preferences.getFloat("Rolverbruik", 0);
  Totaal_rotaties = preferences.getUInt("Totaal_rotaties", 0);
  Laatste_rotatie = preferences.getULong("Laatste_rotatie", 0);
  Totaal_rotatietijd = preferences.getULong("Totaal_rotatietijd", 0);

  connectWiFi();
  syncTime();

  // Create tasks for both cores
  xTaskCreatePinnedToCore(handleLogic, "handleLogic", 4096, NULL, 1, NULL, 0);      // Core 0
  xTaskCreatePinnedToCore(handleDisplay, "handleDisplay", 4096, NULL, 1, NULL, 1);  // Core 1
}
void loop() {
  // Main code to run repeatedly here
}

Don't pin the tasks and try again:
xTaskCreate(...);

Then the project you're attempting is probably beyond your current abilities and starting with crappy AI-generated code is not going to help. How much of that code do you understand?

Jumping in with a big code that you don't understand is not going to end well. Usually, one starts by coding and debugging one feature at a time. Only move on to the next feature when the previous ones are working perfectly. That way if something breaks, you can be pretty sure it was the last thing you added.

Some things I would try:

Start by running the ESP32 Exception Decoder and posting the results.

Next, since the error was detected on Core 0, get all your code off that core:

Pin both tasks to Core 1 and leave Core 0 for the WiFi subsystem. If the exception moves to Core 1, at least we'll know that it's your code that's screwed up and not something you did to the WiFi.

Post the results.

Thanks for your reply. I am going to try this. The code is mostly written by myself. And I understand what it should do. Only some parts are copied/pasted, like the WiFi bit. The parts I still use from the code ChatGPT made are the running on 2 cores (which admittedly I don’t understand).

well it is not the wifi, cause it connects through setup and is not sent to a core. Could that be the issue? Do I need to assign a core?

I can compile fine in IDE2.3.2. But there I cant use the ESP Exception Decoder. Compiling in IDE 1.8.57 doesnt work and the error message is like 10 pages long. I dont know why. Same code and same Libraries.

IMO task pinning should be reserved for the experienced user. Newbies tend to run into a jungle of problems.

I understand what you are saying and am gratefull for you first post with tips. And partially I agree, cause I seem to be in over my head. On the otherside, there is only one way to get more experienced and that is to try new things. So tips are far more helpfull then telling me what I shoudl try or shouldn't, it is far more helpfull when someone helps to find a sollution.

As I hopefully explained, I want simultaneous operation of TFT display and the other actions. That is why a 2 core sollution fits best. That I have no experience in this, shouldnt matter, cause in my opinion it is the best sollution and so I have to try to get it right and gain the experience.

There is a reason for 2 tasks, but certainly not for 2 cores. Consider a bigger project with 3 or more tasks - would you buy a 4 core controller for it?

Your general problem are polling tasks - these make the watchdog bark as you already have learned. Add delays or yields or wait for events in each task in order to allow other tasks running in the meantime.

That's incorrect. The bulk of the ESP32's WiFi system runs in the background on Core 0. All you see in setup() and loop() is the interface to it.

What happened when you moved both tasks to Core 1 as I suggested? Post the resulting error messages from that test.

There is no IDE 1.8.57. The latest (before the switch to IDE 2.x) is v1.8.19. Try compiling with that. Make sure you're using the exact same version of the Arduino ESP32 Core and the exact same versions of all libraries.

That's not due to assigning tasks to cores, it's because they don't understand the multi-tasking programming paradigm. It requires a significantly different mindset. This code is simply not written properly for the multi-tasking environment. In fact, the possibility of user code running on both cores adds another level of complexity.

True. I recommend you start that journey by reading the book Mastering the FreeRTOS Real Time Kernel from here: https://www.freertos.org/Documentation/RTOS_book.html.
You can skip the first two chapters.

But, IMO, chances of getting this to work with just simple fixes are pretty slim. It's just not structured correctly. It's also far too large to debug now. If you really want it to work with multi-tasking, it will probably need to be rewritten from scratch ... with no garbage from chatGPT.

This is likely not a watch dog problem as the first line of the error message says IllegalInstruction. It's more likely something like mishandling a pointer, accessing beyond the end of an array, or maybe inadequate stack space.

Right maybe is 2 cores out of my league, but i tried. I got my code working just fine without the split over cores. But I want to display realtime countdowns when the resetbutton is pressed or a switch changes state, or show the direction of motor when a button is pushed. How do I do that without using multiple cores?

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <WiFi.h>
#include <time.h>
#include <Preferences.h>
#include <Adafruit_NeoPixel.h>

// Pin definitions
#define LED_PIN 48  // Builtin LED
#define NUM_LEDS 1
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

#define FLOAT_SWITCH_PIN 4
#define RESET_BUTTON_PIN 41
#define CW_BUTTON_PIN 42
#define CCW_BUTTON_PIN 40
#define CW_MOTOR_PIN 15
#define CCW_MOTOR_PIN 16
#define TFT_CS 9
#define TFT_RST 46
#define TFT_DC 3
#define TFT_MOSI 8
#define TFT_SCK 18
#define TFT_LED 17
#define GREEN_LED_PIN 39
#define YELLOW_LED_PIN 38
#define RED_LED_PIN 37

//Setup definities
const char *ssid = "The Incredibles";
const char *password = "@Anjer 35";
const char *ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 3600;      // UTC +1 (CET)
const int daylightOffset_sec = 3600;  // 1 hour for daylight saving time (CEST)
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCK, TFT_RST);
Preferences preferences;

//Variabelen
unsigned long Tijd_rotatie = 10000;
unsigned long Rotatie_reductie = 10;
float Afst_rotatie = 0.50195;  //cm
unsigned long Float_tijd = 10000;
unsigned long Reset_knop = 5000;
float Rolverbruik = 0;
int Rollengte = 2500;  //cm
float Totaal_rotaties = 0;
unsigned long Laatste_rotatie = 0;
unsigned long Totaal_rotatietijd = 0;
unsigned long Gem_rotatietijd = 0;
bool displayUpdate = true;
int seconds_left = 0;
bool motorRunning = false;
bool motorDirectionCW = true;  // true for CW, false for CCW
unsigned long start_tijd = 0;
unsigned long duur = 0;
float verbruik = 0;
unsigned long last_rotation_time = 0;

void (*resetFunc)(void) = 0;

void connectWiFi() {
  WiFi.begin(ssid, password);
  int attempts = 0;
  int max_attempts = 10;  // Maximum number of attempts before giving up
  bool connected = false;

  while (attempts < max_attempts && !connected) {
    delay(5000);  // Wait 5 seconds before checking connection status
    attempts++;

    if (WiFi.status() == WL_CONNECTED) {
      connected = true;
    }
  }

  if (connected) {
    uint8_t brightness = 100;
    strip.setBrightness(brightness);
    strip.setPixelColor(0, strip.Color(0, 0, 255));  // Blue
    strip.show();
    delay(1000);
  } else {
    uint8_t brightness = 255;
    strip.setBrightness(brightness);
    strip.setPixelColor(0, strip.Color(255, 0, 0));  // Red
    strip.show();
    delay(5000);
    // Here you could implement a fallback mechanism or retry logic if needed
  }
}
void syncTime() {
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  struct tm timeinfo;
  if (getLocalTime(&timeinfo)) {
    uint8_t brightness = 100;
    strip.setBrightness(brightness);
    strip.setPixelColor(0, strip.Color(0, 255, 0));  // Green
    strip.show();
    delay(1000);  // Turn on yellow LED if time synchronized
  } else {
    uint8_t brightness = 255;
    strip.setBrightness(brightness);
    strip.setPixelColor(0, strip.Color(255, 255, 0));  // Yellow
    strip.show();
    delay(5000);
  }
}
String getTime() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    return "N/A";
  }
  char buffer[10];
  strftime(buffer, sizeof(buffer), "%H:%M:%S", &timeinfo);
  return String(buffer);
}
String getDate() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    return "N/A";
  }
  char buffer[12];
  strftime(buffer, sizeof(buffer), "%d-%m-%Y", &timeinfo);
  return String(buffer);
}
void formatDuration(unsigned long duration, int &days, int &hours, int &minutes, int &seconds) {
  days = duration / 86400;
  duration %= 86400;
  hours = duration / 3600;
  duration %= 3600;
  minutes = duration / 60;
  seconds = duration % 60;
}
void displayBasis() {
  static unsigned long float_schakel_tijd2 = 0;                   // Ensure this is static to maintain value between function calls
  bool floatSwitchState2 = digitalRead(FLOAT_SWITCH_PIN) == LOW;  // Reading the state of the float switch
  unsigned long current_time2 = millis();                         // Get the current time

  if (displayUpdate == true) {
    tft.fillScreen(ST77XX_BLACK);
    displayUpdate = false;
  } else {
    tft.setCursor(10, 10);
    tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
    tft.setTextSize(2);
    tft.print(getTime());
    tft.setCursor(150, 10);
    tft.print(getDate());

    tft.setTextSize(1);
    tft.setCursor(10, 30);
    tft.print("Verbruik rol: ");
    tft.setCursor(150, 30);
    tft.print(Rolverbruik, 2);

    tft.setCursor(10, 50);
    tft.print("Aantal rotaties: ");
    tft.setCursor(150, 50);
    tft.print(Totaal_rotaties, 1);

    unsigned long current_time = millis();
    int days, hours, minutes, seconds;
    // Duration since last rotation
    unsigned long duration_since_last_rotation = (current_time - last_rotation_time) / 1000;
    formatDuration(duration_since_last_rotation, days, hours, minutes, seconds);
    tft.setCursor(10, 70);
    tft.print("Sinds laatste rotatie: ");
    tft.setCursor(150, 70);
    tft.printf("%02d:%02d:%02d\n", days, hours, minutes);

    int days1, hours1, minutes1, seconds1;
    // Duration since last rotation
    if (Totaal_rotaties >= 1) {
    unsigned long Gem_rotatietijd = (Totaal_rotatietijd / Totaal_rotaties);}
    formatDuration(Gem_rotatietijd, days1, hours1, minutes1, seconds1);
    tft.setCursor(10, 90);
    tft.print("Gem. tijd rotatie: ");
    tft.setCursor(150, 90);
    tft.printf("%02d:%02d:%02d\n", days1, hours1, minutes1);
  }
}
void displayReset() {
  if (displayUpdate == true) {
    tft.fillScreen(ST77XX_BLACK);
    displayUpdate = false;
  } else {
    tft.setCursor(150, 100);
    tft.setTextColor(ST77XX_RED, ST77XX_BLACK);
    tft.setTextSize(2);
    tft.print(seconds_left);
  }
  if (seconds_left <= 0) {
    unsigned long reset_complete_time = millis() + 5000;
    while (millis() < reset_complete_time) {
      // Do nothing, wait for 5 seconds
    };
    tft.fillScreen(ST77XX_BLACK);
    displayBasis();
  }
}
void startMotorKlok() {
  digitalWrite(CW_MOTOR_PIN, HIGH);
  digitalWrite(CCW_MOTOR_PIN, LOW);
}
void startMotorTegen() {
  digitalWrite(CW_MOTOR_PIN, LOW);
  digitalWrite(CCW_MOTOR_PIN, HIGH);
}
void stopMotor() {
  digitalWrite(CW_MOTOR_PIN, LOW);
  digitalWrite(CCW_MOTOR_PIN, LOW);
}
void motorKnoppen() {
  bool cwButtonState = digitalRead(CW_BUTTON_PIN) == LOW;    // Controleer of CW knop ingedrukt is
  bool ccwButtonState = digitalRead(CCW_BUTTON_PIN) == LOW;  // Controleer of CCW knop ingedrukt is
  unsigned long current_time = millis();                     // Huidige tijd verkrijgen

  if (cwButtonState && ccwButtonState) {
    // Beide knoppen tegelijkertijd ingedrukt
    tft.fillScreen(ST77XX_BLACK);
    tft.setCursor(10, 100);
    tft.setTextColor(ST77XX_RED);
    tft.setTextSize(3);
    tft.print("REBOOT");
    displayUpdate = true;
    delay(5000);
    resetFunc();  // Reset Arduino (vereist een functie resetFunc() die elders gedefinieerd is)
  } 
  else if (cwButtonState) {
    // Alleen CW knop ingedrukt
    if (!motorRunning || !motorDirectionCW) {
      start_tijd = current_time;  // Tijd bijhouden wanneer motor is gestart
      startMotorKlok();           // Start de motor met de klok mee
      motorRunning = true;
      motorDirectionCW = true;
    }
  }
  else if (ccwButtonState) {
    // Alleen CCW knop ingedrukt
    if (!motorRunning || motorDirectionCW) {
      start_tijd = current_time;  // Tijd bijhouden wanneer motor is gestart
      startMotorTegen();          // Start de motor tegen de klok in
      motorRunning = true;
      motorDirectionCW = false;
    }
  } else {
    // Geen knoppen ingedrukt
    if (motorRunning) {
      duur = current_time - start_tijd;  // Bereken de verstreken tijd
      verbruik = Afst_rotatie * (duur / (float)Tijd_rotatie);  // Bereken verbruik
      if (motorDirectionCW) {
        Rolverbruik += verbruik;  // Update totaal verbruik
        Totaal_rotaties += (verbruik / Afst_rotatie);
      } else {
        Rolverbruik -= verbruik; 
        if (Rolverbruik <= 0){Rolverbruik = 0;} // Update totaal verbruik (aftrekken bij tegen de klok in)
        Totaal_rotaties -= (verbruik / Afst_rotatie);
        if (Totaal_rotaties <= 0){Totaal_rotaties = 0;}
      }
      stopMotor();  // Stop de motor
      motorRunning = false;
      duur = 0;  // Reset de tijd
    }
  }
}
void floatSwitch() {
  static unsigned long float_schakel_tijd = 0;                   // Ensure this is static to maintain value between function calls
  bool floatSwitchState = digitalRead(FLOAT_SWITCH_PIN) == LOW;  // Reading the state of the float switch
  unsigned long current_time = millis();                         // Get the current time

  if (floatSwitchState) {  // If the float switch is LOW
    if (float_schakel_tijd == 0) {
      float_schakel_tijd = current_time;  // Set the switch time
    } else if (current_time - float_schakel_tijd >= Float_tijd) {
      startMotorKlok();        // Start the motor
      delay(Tijd_rotatie);      // Wait for the rotation duration
      stopMotor();             // Stop the motor
      float_schakel_tijd = 0;  // Reset the switch time
      Totaal_rotaties++;
      Rolverbruik += Afst_rotatie;
      Totaal_rotatietijd += (current_time - last_rotation_time);
      last_rotation_time = current_time;
    }
  } else {                   // If the float switch is HIGH
    float_schakel_tijd = 0;  // Reset the switch time
  }
}
void resetKnop() {
  static unsigned long reset_druk_tijd = 0;
  bool ResetButtonState = digitalRead(RESET_BUTTON_PIN) == LOW;
  unsigned long current_time = millis();

  if (ResetButtonState) {
    if (reset_druk_tijd == 0) {
      reset_druk_tijd = current_time;
      displayUpdate = true;
      displayReset();
    } else {
      seconds_left = (Reset_knop - (current_time - reset_druk_tijd)) / 1000;  // Update the existing seconds_left variable

      if (current_time - reset_druk_tijd >= Reset_knop) {
        Rolverbruik = 0;
        Totaal_rotaties = 0;
        Laatste_rotatie = 0;
        Totaal_rotatietijd = 0;
        preferences.putFloat("Rolverbruik", Rolverbruik);
        preferences.putUInt("Totaal_rotaties", Totaal_rotaties);
        preferences.putULong("Laatste_rotatie", Laatste_rotatie);
        preferences.putULong("Totaal_rotatietijd", Totaal_rotatietijd);
        tft.fillScreen(ST77XX_BLACK);
        tft.setCursor(10, 100);
        tft.setTextColor(ST77XX_RED);
        tft.setTextSize(2);
        tft.print("RESET COMPLETE");
        delay(5000);
        resetFunc();

      } else {
        displayUpdate = true;
        displayBasis();
        reset_druk_tijd = 0;
      }
    }
  }
}
void LEDS() {
  if (Rolverbruik < 0.75 * Rollengte) {
    digitalWrite(GREEN_LED_PIN, HIGH);
    digitalWrite(YELLOW_LED_PIN, LOW);
    digitalWrite(RED_LED_PIN, LOW);
  } else if (Rolverbruik < 0.9 * Rollengte) {
    digitalWrite(GREEN_LED_PIN, LOW);
    digitalWrite(YELLOW_LED_PIN, HIGH);
    digitalWrite(RED_LED_PIN, LOW);
  } else {
    digitalWrite(GREEN_LED_PIN, LOW);
    digitalWrite(YELLOW_LED_PIN, LOW);
    digitalWrite(RED_LED_PIN, HIGH);
  }
}
void Rotatiereductie(){
  if(Totaal_rotaties >=1){
    Tijd_rotatie = (Rotatie_reductie * Totaal_rotaties);
  }
}
void setup() {
  Serial.begin(115200);
  pinMode(FLOAT_SWITCH_PIN, INPUT_PULLUP);
  pinMode(RESET_BUTTON_PIN, INPUT_PULLUP);
  pinMode(CW_BUTTON_PIN, INPUT_PULLUP);
  pinMode(CCW_BUTTON_PIN, INPUT_PULLUP);
  pinMode(CW_MOTOR_PIN, OUTPUT);
  pinMode(CCW_MOTOR_PIN, OUTPUT);
  pinMode(GREEN_LED_PIN, OUTPUT);
  pinMode(YELLOW_LED_PIN, OUTPUT);
  pinMode(RED_LED_PIN, OUTPUT);
  digitalWrite(TFT_LED, HIGH);

  strip.begin();
  strip.show();  // Initialize all pixels to 'off'

  tft.init(240, 320);
  tft.invertDisplay(0);
  tft.setRotation(1);

  preferences.begin("my-app", false);
  Rolverbruik = preferences.getFloat("Rolverbruik", 0);
  Totaal_rotaties = preferences.getUInt("Totaal_rotaties", 0);
  Laatste_rotatie = preferences.getULong("Laatste_rotatie", 0);
  Totaal_rotatietijd = preferences.getULong("Totaal_rotatietijd", 0);

  connectWiFi();
  syncTime();
  strip.setPixelColor(0, strip.Color(0, 0, 0));  // off
  strip.show(); 
}
void loop() {
  displayBasis();
  LEDS();
  motorKnoppen();
  floatSwitch();
  resetKnop();
  Rotatiereductie();
}

You should use the standard Arduino non-blocking, millis-based multitasking techniques. You can do an internet search and find many examples and tutorials. Here are just a few:
https://forum.arduino.cc/t/arduino-multitasking-tutorial-how-to-use-millis-in-arduino-code/653971

https://circuitdigest.com/microcontroller-projects/arduino-multitasking-using-millis-in-arduino

https://forum.arduino.cc/t/example-code-for-timing-based-on-millis-easier-to-understand-through-the-use-of-example-numbers-avoiding-delay/974017

Thanks for the tip, I'll look into this, I have used it in the past.

The thing is, I dont use many delays. But when I press a button, it seems to stay in that part of my code till a certain criteria is met (button release or time has passed). This causes the display not to show a countdown/ counter or button state. This is without the delay();. Can i fix this with currentmillis / millis ()? I know the method, I have used it before in tests and tutorials. But I dont see how to incorporate it in my code.

Can you give an example for this bit?

void resetKnop() {
  static unsigned long reset_druk_tijd = 0;
  bool ResetButtonState = digitalRead(RESET_BUTTON_PIN) == LOW;
  unsigned long current_time = millis();

  if (ResetButtonState) {
    if (reset_druk_tijd == 0) {
      reset_druk_tijd = current_time;
      displayUpdate = true;
      displayReset();
    } else {
      seconds_left = (Reset_knop - (current_time - reset_druk_tijd)) / 1000;  // Update the existing seconds_left variable

      if (current_time - reset_druk_tijd >= Reset_knop) {
        Rolverbruik = 0;
        Totaal_rotaties = 0;
        Laatste_rotatie = 0;
        Totaal_rotatietijd = 0;
        preferences.putFloat("Rolverbruik", Rolverbruik);
        preferences.putUInt("Totaal_rotaties", Totaal_rotaties);
        preferences.putULong("Laatste_rotatie", Laatste_rotatie);
        preferences.putULong("Totaal_rotatietijd", Totaal_rotatietijd);
        tft.fillScreen(ST77XX_BLACK);
        tft.setCursor(10, 100);
        tft.setTextColor(ST77XX_RED);
        tft.setTextSize(2);
        tft.print("RESET COMPLETE");
        delay(5000);
        resetFunc();

      } else {
        displayUpdate = true;
        displayBasis();
        reset_druk_tijd = 0;
      }
    }
  }
}

Put that in a high priority task that sleeps until the next update (countdown) of the display. You also can send messages to that task from all other tasks which may detect a need for a display update.

You provide the tasks and the RTOS distributes the tasks across the available cores.

You claim you "dont use many delays", yet that code has a 5-second delay in it!!!

You need to adopt the Finite State Machine programming paradigm. This is where you divide work that must be done into small pieces that run quickly. Then you step through them. At any step, you check to see if it's time to do one of those pieces (based on millis-timing). If so, do it. Otherwise move to the next piece. Either way, you spend very little time on it before moving to check if it's time to do another piece of work.

There are countless tutorials covering this concept:
https://forum.arduino.cc/t/yet-another-finite-state-machine-introduction/1198997

https://forum.arduino.cc/t/a-demo-code-explaining-the-switch-case-state-machine-and-how-to-do-things-almost-in-parallel/888172/1

Adafruit has a 3-part series stating here:
https://learn.adafruit.com/multi-tasking-the-arduino-part-1/overview

The bottom line is that whether or not you go with classic "Arduino Multitasking" or use a multitasking operating system like the ESP32's FreeRTOS, your code needs to be restructured or completely rewritten. Of the two choices, FreeRTOS is the more sophisticated technique but it has a much steeper learning curve before you can do it correctly.

1 Like

But this is correct, the delay occurs before a reset, where I want just one message on display and not other actions. I meant I dont use delays where I dont want full code to stop. It is by intention when I do put in delays.

BlockquoteYou need to adopt the Finite State Machine programming paradigm. This is where you divide work that must be done into small pieces that run quickly. Then you step through them. At any step, you check to see if it's time to do one of those pieces (based on millis-timing). If so, do it. Otherwise move to the next piece. Either way, you spend very little time on it before moving to check if it's time to do another piece of work

this is very helpfull. I dont know all the terminology so searching is dificult. Thanks for the tip!

And it's the recommended procedure in FreeRTOS. The system does not block but suspends the delayed task and allows other tasks to run on the now unused core.

I had the same error, in my case the problem was that something on the board library does not work properly. I downgrade the board library to version 2.0.14 and there were no issues at all and my code compiled and run perfectly

1 Like

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