ATtiny1614 weird behavior after a few days

Hi guys, I am experiencing some really weird behavior with a project of mine that uses the ATtiny1614 and I would really appreciate your help.
This project is an analog Wah-Wah guitar effect that is controlled by a digital potentiometer.
Basically here is how it works. The effect has 3 modes that are controlled using an optocoupler.
Mode 1 is Autowah and the coupler is OFF, removing the Digipot from the circuit.

Mode 2 is normal wah with a expression pedal connected to an analog input of the ATtiny, and controls the Digipot via SPI. In this mode the coupler is ON and inserts the Digipot in the analog circuit.

Mode 3 is a mix of both modes and the coupler is OFF when the pedal all back, but as soon as it moves a bit it turns ON and the Digipot reacts to it.

There is a button that cycle between modes with a short press and saves the current mode to the eeprom with a long press.
When cycling the modes the LED blinks as many times as the mode number, and when the mode is saved, it blinks a few times but faster.
There is also a general settings menu that can be called by pressing this button long at power on. It has 4 options (1 to 4). A short press cycles between options and the LED blinks the option number. When the button is pressed long, the option function is executed:

Option 1 - Control Type, Expression Pedal or Midi. Press Button long to change and save to eeprom.

Option2 - Pedal Reversed or Normal. Press Button long to change and save to eeprom.

Option3 - Pedal Calibration. Press Button long to initiate calibration, LED Blinks fast, Move pedal to both ends, after a few seconds calibration is finished and values are saved to the eeprom, LED blinks fast a few times again to indicate end of calibration.

Option4 - Press the button long to Exit General settings and resume normal operation.

Also, at Power ON, still in Setup and after reading the eeprom, the Code should blink the LED as many times as the Mode number saved on the EEprom, and then go into the main loop.

The Problem - After I flash it, it works fine for a few days and then suddenly it stops reacting to the expression pedal, it doesn't indicate the Mode number by Blinking the LED at power ON, and when I save a new mode it returns to the previous mode after cycling the power, meaning that it either didn't save the new mode number to the eeprom, or it is not reading it at power ON. It behaves like the pedal got stuck at the lower end, so for some reason is not reading the ADC, or not updating the digipot. But why would it also stop blinking the mode number at power ON? Weird
Normally to fix this I need to flash the code to the chip again, but today I managed to fix it by just changing it from mode 3 to mode 2 and saving it. It didn't work when I changed from mode 3 to Mode 1 and saved though.
as I said this is a very weird problem and it doesn't happen all the time. I still couldn't find out what triggers this behavior. Here is the entire code and thanks in advance for your help:


#include <SPI.h>
#include <EEPROM.h>
#include "HystFilter.h"

HystFilter potA(1024, 128, 4);  // Input 12 bit ADC = 4096, 128 discrete output values required, margin = 8 units (of 4096)




// Which pin is connected to CS
const uint8_t CS_PIN = 7;
const int PotPin = 0;  // Analog input pin that the potentiometer is attached to
int Led = 2;
int Opto = 6;
bool OptoState = LOW;
bool Old_OptoState;
int Trigger = 3;

int External_Input = 0;
int Reverse = 0;
int ControlType = 0;


int PotValue = 0;     // value read from the pot
int OutputValue = 0;  // value output to the PWM (analog out)
int Last_OutputValue = 0;
int Last_PotValue = 0;

int sensorMin = 127;  // minimum sensor value
int sensorMax = 0;    // maximum sensor value
int last_val_scaled;
int val_scaled;
const unsigned long calib_time = 6000;

int MuteDelayON = 30;
int MuteDelayOFF = 0;

int MidiChannel;
bool Bypass = false;

byte state = 0;
byte statusByte;
byte dataByte1;
byte dataByte2;
byte channel;
byte incomingByte;

byte number;
byte value;

byte velocity = 0;
int old_velocity = -1;
byte program;

int Reset_Eeprom;

long onDuration = 100;   // ON time for the Led (in milliseconds)
long offDuration = 500;  // OFF time for the Led (in milliseconds)
bool ledState = LOW;     // Initial state of the Led
long rememberTime = 0;   // Used by the code
int blinkCount = 5;      // Number of times to blink the Led
bool BlinkActive = false;

int Counter = 0;

const int numberButtons = 1;
const int button[numberButtons] = { 1};
const int buttons[] = { 1};
int pressed[numberButtons], justReleased[numberButtons], justpressed[numberButtons], ShortPress[numberButtons], LongPress[numberButtons], XtraLongPress[numberButtons];

static long Timestart;
boolean ButtonWasPressed = false;

const int debounceDelay = 3;                    // the debounce time in milliseconds
unsigned long lastDebounceTime[numberButtons];  // the last time the button was debounced
bool buttonState[numberButtons];
bool lastButtonState[numberButtons];

int Byp = 1;
int AutoButton = 0;


void setup() {

  SPI.begin();

  for (int x = 0; x < numberButtons; x++) {
    pinMode(button[x], INPUT_PULLUP);
  }
  for (int i = 0; i < numberButtons; i++) {
    buttonState[numberButtons] = LOW;
    lastButtonState[numberButtons] = LOW;
  }

  pinMode(Led, OUTPUT);
  pinMode(Opto, OUTPUT);
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH);
  digitalWrite(Led, LOW);
  digitalWrite(Opto, LOW);


  External_Input = EEPROM.read(0);
  Reverse = EEPROM.read(1);
  ControlType = EEPROM.read(2);
  sensorMax = EEPROM.read(3);
  sensorMin = EEPROM.read(4);
  MuteDelayON = EEPROM.read(5);
  MuteDelayOFF = EEPROM.read(6);
  Reset_Eeprom = EEPROM.read(10);

  if (Reset_Eeprom != 69) {  // If EEprom is not valid, format it
    for (int i = 0; i < 256; i++) {
      EEPROM.write(i, 0);
      delay(2);
    }
    EEPROM.write(10, 69);
    EEPROM.write(3, 127);
    EEPROM.write(4, 0);

    Blink_LED(12, 40, 100);  // (Nr of Blinks, ON Time ms, OFF Time ms)
  }

  if (digitalRead(button[AutoButton]) == LOW) {

    StartupModes();
  }

Blink_LED(ControlType + 1, 40, 250);  // (Nr of Blinks, ON Time ms, OFF Time ms)

  Serial.begin(31250);

  if(External_Input == 0) {
    PotValue = potA.getOutputLevel(analogRead(PotPin));
    
      Send_PotValue();
  }

  if (ControlType == 0) {  // Auto Wah Mode
    OptoState = LOW;
  }

  if (ControlType == 1) {  // Pedal Wah Mode
    OptoState = HIGH;
  }
  HandleOpto();

  // digitalWrite(MutePin, LOW);
}


void loop() {
  MidiIn();
  check_switches();


  if (External_Input == 0) {
    PotValue = potA.getOutputLevel(analogRead(PotPin));

    if (PotValue != Last_PotValue) {
      Send_PotValue();
    }
    Last_PotValue = PotValue;
  }


  if (ShortPress[AutoButton]) {
    ControlType++;
    if (ControlType > 2) ControlType = 0;


    if (ControlType == 0) {  // Auto Wah Mode
      OptoState = LOW;
    }

    if (ControlType == 1) {  // Pedal Wah Mode
      OptoState = HIGH;
    }

    if ((ControlType == 2) and (OutputValue < Trigger)) {  // Pedal Wah Mode
      OptoState = LOW;
    }

    HandleOpto();

    Blink_LED(ControlType + 1, 40, 250);  // (Nr of Blinks, ON Time ms, OFF Time ms)
  }
  if (XtraLongPress[AutoButton]) {
    EEPROM.write(2, ControlType);
    Blink_LED(5, 60, 80);  // (Nr of Blinks, ON Time ms, OFF Time ms)
  }

  if (ControlType == 2) {
    if (OutputValue >= Trigger) {
      OptoState = HIGH;
      // if ((OptoState == LOW) && (OptoState != Old_OptoState)) {
      if (OptoState != Old_OptoState) {

        HandleOpto();
        Old_OptoState = OptoState;
      }
    }

    if (OutputValue < Trigger) {
      OptoState = LOW;
      // if ((OptoState == HIGH) && (OptoState != Old_OptoState)) {
      if (OptoState != Old_OptoState) {
        HandleOpto();
        Old_OptoState = OptoState;
      }
    }
  }

  if (BlinkActive) {

    // THIS ONE STARTS AND ENDS WITH LED HIGH

    if (blinkCount > 0) {
      if (ledState == LOW) {
        if ((millis() - rememberTime) >= offDuration) {
          ledState = HIGH;          // Change the state of the LED
          rememberTime = millis();  // Remember the current millis() time
        }
      } else {
        if ((millis() - rememberTime) >= onDuration) {
          ledState = LOW;           // Change the state of the LED
          rememberTime = millis();  // Remember the current millis() time
          blinkCount--;             // Decrease the blink count
        }
      }
      digitalWrite(Led, ledState);  // Turn the LED ON or OFF

    } else {
      BlinkActive = false;
    }
  }
}

void HandleOpto() {
  if (OptoState == LOW) {  // Auto Wah Mode
    digitalWrite(Opto, OptoState);

    // Serial.println("Auto");
  }

  if (OptoState == HIGH) {  // Pedal Wah Mode
    digitalWrite(Opto, OptoState);

    // Serial.println("Pedal");
  }
}

void Send_MIDI_Value() {
  // Reverse = EEPROM.read(0);

  if (Reverse == 0) {
    // map it to the range of the analog out:
    OutputValue = map(velocity, 0, 127, 0, 127);
  } else if (Reverse == 1) {
    OutputValue = map(velocity, 0, 127, 127, 0);
  }

  OutputValue = constrain(OutputValue, 0, 127);

  MCP41HV_Write(CS_PIN, OutputValue);
}

void Send_PotValue() {
  if (Reverse == 0) {
    // map it to the range of the analog out:
    OutputValue = map(PotValue, sensorMin, sensorMax, 0, 127);
  } else if (Reverse == 1) {
    OutputValue = map(PotValue, sensorMin, sensorMax, 127, 0);
  }


  OutputValue = constrain(OutputValue, 0, 127);

  // Serial.println(OutputValue);

  MCP41HV_Write(CS_PIN, OutputValue);
}

int MCP41HV_Write(int pin, int value) {
  digitalWrite(pin, LOW);  //When Slave is LOW, the MCP listens to the master
  SPI.transfer(0x00);
  SPI.transfer(value);
  digitalWrite(pin, HIGH);  //When Slave is HIGH, the MCP does not listen to the master
}

void StartupModes() {
  bool StayingInside = true;
  Blink_LED(6, 60, 80);  // (Nr of Blinks, ON Time ms, OFF Time ms)

  while (digitalRead(button[AutoButton]) == LOW) {
  }
  delay(500);
  Blink_LED(Counter + 1, 70, 300);  // (Nr of Blinks, ON Time ms, OFF Time ms)

  while (StayingInside) {
    check_switches();
    Do_The_Blink();

    for (int tmp = 0; tmp < numberButtons; tmp++) {

      if (ShortPress[tmp]) {
        if (tmp == AutoButton) {
          Counter++;
          if (Counter > 3) Counter = 0;
          Blink_LED(Counter + 1, 70, 300);  // (Nr of Blinks, ON Time ms, OFF Time ms)
        }
      }

      if (XtraLongPress[tmp]) {
        if (tmp == AutoButton) {
          if (Counter == 0) {  // Cofigure PowerON State

            External_Input = !External_Input;
            EEPROM.write(0, External_Input);

            Blink_LED(6, 60, 80);  // (Nr of Blinks, ON Time ms, OFF Time ms)
          }

          if (Counter == 1) {  // Cofigure PowerON State
            Reverse = !Reverse;
            EEPROM.write(1, Reverse);

            Blink_LED(6, 60, 80);  // (Nr of Blinks, ON Time ms, OFF Time ms)
          }

          if (Counter == 2) {  // Cofigure MuteDelayON
            Pedal_Calibration();
            Blink_LED(6, 60, 80);  // (Nr of Blinks, ON Time ms, OFF Time ms)
          }

          if (Counter == 3) {  // Exit
            StayingInside = false;
            Blink_LED(6, 60, 80);  // (Nr of Blinks, ON Time ms, OFF Time ms)
          }
        }
        //        StayingInside = false;
      }
    }

  }

  External_Input = EEPROM.read(0);
  Reverse = EEPROM.read(1);
  sensorMax = EEPROM.read(3);
  sensorMin = EEPROM.read(4);
}

void Pedal_Calibration() {
  sensorMax = 0;
  sensorMin = 127;

  Blink_LED(3, 60, 80);  // (Nr of Blinks, ON Time ms, OFF Time ms)
  while (BlinkActive) {
    Do_The_Blink();
  }


  uint16_t startMillis = millis();


  uint16_t currentMillis = millis();
  // calibrate during the first five seconds
  while (currentMillis - startMillis < calib_time) {

    int Pot_val = potA.getOutputLevel(analogRead(PotPin));

    // record the maximum sensor value
    if (Pot_val > sensorMax) {
      sensorMax = Pot_val;
    }

    // record the minimum sensor value
    if (Pot_val < sensorMin) {
      sensorMin = Pot_val;
    }

    currentMillis = millis();

    // delay(2);
  }
  // signal the end of the calibration period

  delay(20);

  EEPROM.write(3, sensorMax);
  EEPROM.write(4, sensorMin);

  // Blink_LED(2);
}

void Blink_LED(byte Count, const long On, const long Off) {

  blinkCount = Count;
  onDuration = On;
  offDuration = Off;
  BlinkActive = true;
}

void Do_The_Blink() {
  if (BlinkActive) {
    // THIS ONE STARTS AND ENDS WITH LED LOW
    if (blinkCount > 0) {
      if (ledState == LOW) {
        if ((millis() - rememberTime) >= offDuration) {
          ledState = HIGH;          // Change the state of the LED
          rememberTime = millis();  // Remember the current millis() time
        }
      } else {
        if ((millis() - rememberTime) >= onDuration) {
          ledState = LOW;           // Change the state of the LED
          rememberTime = millis();  // Remember the current millis() time
          blinkCount--;             // Decrease the blink count
        }
      }
      digitalWrite(Led, ledState);  // Turn the Led ON or OFF

    } else {
      BlinkActive = false;
    }
  }
}

void check_switches() {
  static byte previousstate[numberButtons];
  static byte currentstate[numberButtons];
  static long lasttime;
  static long Timelapse = 500;
  static long TimelapseLong = 500;
  //  static long Timestart;
  int index;

  for (index = 0; index < numberButtons; index++) {
    justReleased[index] = 0;
    justpressed[index] = 0;  //when we start, we clear out the "just" indicators
    ShortPress[index] = 0;
    LongPress[index] = 0;
    XtraLongPress[index] = 0;
  }

  for (index = 0; index < numberButtons; index++) {
    int reading = digitalRead(buttons[index]);

    // check if the button state has changed
    if (reading != lastButtonState[index]) {
      lastDebounceTime[index] = millis();
    }

    if ((millis() - lastDebounceTime[index]) > debounceDelay) {
      // if the button state has been stable for the debounce delay
      if (reading != buttonState[index]) {
        buttonState[index] = reading;

        currentstate[index] = buttonState[index];  //read the button
        pressed[index] = !currentstate[index];     // If CurrenState is LOW, Pressed is 1 (active)

        if (pressed[index]) ButtonWasPressed = true;

        if (currentstate[index] != previousstate[index]) {

          if ((!pressed[index]) && (ButtonWasPressed)) {
            justReleased[index] = 1;
          }

          if ((pressed[index]) && (previousstate[index] == HIGH)) {  // just pressed
            justpressed[index] = 1;

            Timestart = millis();
          }
          if ((!pressed[index]) && (ButtonWasPressed) && (millis() - Timestart < Timelapse)) {

            justpressed[index] = 0;
            ShortPress[index] = 1;  // just released ShortPress
            ButtonWasPressed = false;
          } else if ((!pressed[index] && ButtonWasPressed) && (millis() - Timestart >= Timelapse) && (millis() - Timestart < TimelapseLong)) {
            justpressed[index] = 0;
            ShortPress[index] = 0;
            LongPress[index] = 1;  // just released LongPress
            ButtonWasPressed = false;
          } else if ((!pressed[index] && ButtonWasPressed) && millis() - Timestart >= TimelapseLong) {
            justpressed[index] = 0;
            ShortPress[index] = 0;
            LongPress[index] = 0;
            XtraLongPress[index] = 1;  // just released XtraLongPress
            ButtonWasPressed = false;
          }
        }
        previousstate[index] = currentstate[index];  //keep a running tally of the buttons
      }
    }
    lastButtonState[index] = reading;
  }
}



void MidiIn() {
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();

    switch (state) {
      case 0:

        channel = (incomingByte & B00001111) + 1;
        if (incomingByte == 0xF9) {
          statusByte = incomingByte;
        } else {
          statusByte = incomingByte & B11110000;
        }

        if (((statusByte > 0x7F) && (incomingByte < 0xF0)) || (incomingByte == 0xF9)) {  // this reads all statusbytes, realtime stuff is ignored

          state = 1;
        }

      case 1:
        // get first databyte
        if (incomingByte < 0x80) {
          dataByte1 = incomingByte;
          // Serial.println(dataByte1);
          //check for two byte messages...
          if ((statusByte & 0xF0) == 0xC0 | (statusByte & 0xF0) == 0xD0) {

            midiParser();

            state = 0;  //reset state machine
          } else state = 2;
        }

        break;

      case 2:
        // get second databyte
        if (incomingByte < 0x80) {
          dataByte2 = incomingByte;
          // Serial.println(dataByte2);
        }
        midiParser();

        state = 0;  // reset state machine to start
        break;
      //stay here for sysex, wait for it to end...
      case 3:
        if (incomingByte == 0xF7) state = 0;
        if (((statusByte > 0x7F) && (incomingByte < 0xF0)) || (incomingByte == 0xF9)) {
          statusByte = incomingByte;
          state = 1;
        }
        break;
    }
  }
}

void midiParser() {

  if (channel == 16) {
    switch (statusByte) {

      case 0xC0:  //program change
        program = dataByte1;


        break;  // End Of Program Change

      case 0xB0:  //cc messages
        number = dataByte1;
        velocity = dataByte2;

        if ((number == 8) and (External_Input == 1)) {

          Send_MIDI_Value();
        }
        break;
    }
  }
}

I have no idea what difference a few days would make, but this may be a problem. Always use unsigned long or uint32_t for millis() and micros() times.

  uint16_t startMillis = millis();


  uint16_t currentMillis = millis();

In glancing through the code looking for array bound violations, etc. I noticed this line:

HystFilter potA(1024, 128, 4); // Input 12 bit ADC = 4096, 128 discrete output values required, margin = 8 units (of 4096)

You might check that library code, considering that the ATtiny1614 has a 10 bit ADC (not 12). Perhaps the comment is misleading.

This is a bit odd, and can lead to indexing confusion:

const int numberButtons = 1;
const int button[numberButtons] = { 1};
const int buttons[] = { 1};

Thanks for your reply. yeah the comment is misleading as you say.
HystFilter potA(1024, 128, 4);
1024 is the ADC resolution and 128 is the number of output steps. Just like mapping.

the other code is the same Buttons code I use on all my projects because I do like the user friendly way that it works. It can use many buttons, but in this case it uses only one button and it is connected to pin 1 of the microcontroller.

I don't think the problem I am seeing is related to this? The button code seems to work well. As I said I also use it in some bigger projects with more than 10 buttons, and it works fine.

My correction and your post crossed. Check above for new addition.

You are absolutely right, of course it should be an unsigned long variable, how did I miss that? :frowning:
But still, How would that mess up the eeprom/saving reading and the ADC? If that provlem affected millis() it would do it after a while, right? but I think millis would start from 0 again after the chip was reset or power cycled, right? Unless that for some reason changed some eeprom value? but how?
I will fix that and test. Thank you. Need to set up a breadboard with a test circuit..

the ones you pointed belong to the pedal calibration function, not a part of my problem, that works fine. I will fix it anyway.

Now I have found these, which are part of the BlinkLED code.

long onDuration = 100;   // ON time for the Led (in milliseconds)
long offDuration = 500;  // OFF time for the Led (in milliseconds)

long rememberTime = 0;   // Used by the code

Would "long" instead of "unsigned long" be a problem?

Thanks.

Very likely. There is no good reason to use signed integers in this instance.

What do you suppose happens here, when millis() exceeds the maximum value of signed long?

rememberTime = millis(); // Remember the current millis() time

Ok thanks. I had a better look at my code and I think that "rememberTime" should be changed to unsigned long, and the other two could be changed to int as they are values the timer is comparing to.. Am I correct here? Please see below.

No, they should be unsigned long. They will be promoted anyway by the compiler.

Rule: always use unsigned long variables and constants with millis() and micros().

onDuration

So, this variable should also be unsigned long even if it just states the number of milliseconds the LED is On? It is not updated by the timer. It is a fixed number, never changes..
Sorry about all these questions, I am quite new to coding..

Why not stick with the rule?

That way you avoid making mistakes that lead to just such time-dependent errors.

You're right.. Better to stick to the rules..
I will test changing this on a breadboard circuit and see if it makes any difference.
Thank you for your help. Will report when I have some results, but it might take a couple days.