Serial monitor=proof…but the µC (Arduino R3) acts differently!

Hello,

It pains me to ask this on the forum but i’v been at it for many hours and i’v tried everything.

Project: A water tank level meter with a I²C 16X2 LCD and a UART waterproof HC-SR04 ultrasonic sensor (Atmega328p-AU=SMD µC) . It’s a one-button operated device (one button=complicated and lots of work). This means one button to alter tank and alarm level setting in different menu’s. Those menu’s are coded with SWITCH/CASE and the device is always in one of the multiple cases. Only two cases call a function to check if a sensor wire is broken.
This function distance() that is called by those two cases needs to check if the sensor is OK in a certain time.
In normal operation the sensor generates about one error every second (=erratic) hence that function distance() must check if the sensor is ok and if not keep checking this with a counter in a timeframe. This means that these few and erratic errors get filtered out. If there has been a sensor error the do/while loop in this distance() function checks the sensor status again.
If a sensor wire brakes these sensor errors keep comming up every time the sensor status is read. In this ‘error’ loop the elapsed time is measured in a timer and the number of errors in a counter. So if the timer (Millis - SensorerrorTime) and counter SensorerrorCounter conditions are met, the loop needs to enter the code in the loop itself to display ‘SENSOR ERROR’ on the lcd display.

To be clear, the rest of the code keeps on working perfectly but it’s 600lines+ long so i’ll just post the relevant code.

Situation:
My project was ready but my new (own design) pcb did not work
The pcb has no USB to UART chip so there is no serial monitoring available.
After looking in to it a bad sensor wire connection was the culprit. That is when i decided to add some extra code to display a error when a sensor wire breaks (principle outline in previous explanation).

Problem and trying to solve it:
Although existing code works on my new pcb (programming with ICSP pins) the added code to detect a sensor error refuses.
First test: I’v tried the relevant code (that does not seems to work); without the rest of the main code; on the Wokwi simulator. On the simulator the reduced code works like a charm (pusbutton replaces the sensor status check).

Next test: Testing the code that i used on the simulator on a real Arduino Uno R3.
This simulation code also works on a real Arduino uno R3 board.

Next test: Testing the project code with the added code on the Arduino uno R3 with the lcd display, ultrasonic sensor and one button connected.
In this test the main code works and the setup is doing everything it should do…exept for the code that needs to display ‘SENSOR ERROR’ on the lcd display when a sensor wire breaks.
The advantage of using the Arduino uno R3 instead of my project pcb means i can use the serial monitoring.
I’m getting a very weird result (the serial monitor proves is) that acualy should be easy peasy.

First an explanation, then the code and serial monitor info with comments to clear it out.
The(Millis - SensorerrorTime) minus operation (of two unsigned long variabels) seems to be not working very good although it should. When serial.print the value’s of those two variables to the serial monitor i can clearly see what the difference is because i’m comparing the outcome to a value with the line (Millis - SensorerrorTime) > 5000.. So this means ‘if the diffence is more then 5000 milliseconds’. In the serial monitor i can clearly see a difference of about 1500ms but the code enters, almost on same time (see timestamp), a if ((Millis - SensorerrorTime) > 5000) {condition while it should not do that. As a result the counter SensorerrorCounter get’s a reset to zero and this is not the intenstion at 1500ms.
So the question is why is this timer not working as it should. The time is very clear in the serial monitor and still it doesn’t fuction as it should.

Now the relevant pieces of code:

Globaly:

unsigned long SensorerrorTime = 0; //setting memory to zero. THis is a timer to check if the sensor is permanently functioning (check in the distance() function) certain amounts of errors in a certain amount of time
uint32_t SensorerrorCounter = 0;   //setting memory to zero. this is a counter to check if the sensor is permanently functioning (check in the distance() function) certain amounts of errors in a certain amount of time

The function that is being called by two cases:

void distance() {      //aside of measuring this function needs to detect a certain amount of sensor not-ok readings in a certain time to keep looping with "SENSOR ERROR" on the display
  DistanceSensor_A02YYUW_MEASSUREMENT_STATUS meassurementStatus;
  unsigned long Millis;
  do {
    meassurementStatus = distanceSensor.meassure(); 
    if (meassurementStatus == DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK) {
      dis = distanceSensor.getDistance()/10;
      SensorerrorCounter = 0; //test with no effect
      Serial.println((String) "reading normal distance=sensor OK");
    } else {                      //this else code is here in case the sensor is dead or a sensor wire is broken
      Serial.println((String) "entering sensorerror else condition=sensor NOT OK"); //for testing
      Millis = millis();
      ++SensorerrorCounter;
      Serial.println(SensorerrorCounter);//testing
      Serial.println(Millis);            //testing
      Serial.println(SensorerrorTime);   //testing
      if ((Millis - SensorerrorTime) > 5000 && SensorerrorCounter > 4) {     //if there are more then 4 sensor errors in 5 sec(5000ms) then the "SENSOR ERROR" message needs to be displayed!!attention shis needs to be 5sec and 4 errors because the µC is to slow (long cycles)                        
           lcd.clear();
           lcd.setCursor(0, 0);  //cursor set line 1, character 0//next line limit info
           lcd.backlight();
           lcd.print("SENSOR ERROR   ");
           Serial.println((String) "conditions SensorerrorTime and SensorerrorCounter");//testing
           SensorerrorTime = millis(); //reset the 1 sec timer to do a new cycle of 1sec to check if sensor doensn't generate a not-ok status
           SensorerrorCounter = 0; //reset counter together with a new 1sec cycle
      } else {
        if ((Millis - SensorerrorTime) > 5000) {
           SensorerrorTime = millis(); //reset the 1 sec timer
           SensorerrorCounter = 0;
           Serial.println((String) "condition SensorerrorTime only");//for testing
        }   
        }    
      }
  }  while (meassurementStatus != DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK); //repeats the code between do and while untill DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK is true
}

Serial monitor (screenshot to get the timestamps):

I know it’s a lot. But i’m grasping.

THX!

Experience here shows that problems are often in the code not posted.

Please post a complete sketch that illustrates the problem

2 Likes

Also, take a picture of a hand-drawn wiring diagram.

Just a quick (Didn't read, too many words) suggestion but if you are doing long math, cast the constants like 5000 as long or better unsigned long as well to avoid any unplanned conversions.

600 lines (and thanks for looking at it). I you see someting unorthodox, i will leave it for know because everything works. And yes lots of global variables, but for all of them i don’t see a possibility of making them local. And lots of passing values is sometimes blocking the oversight.

And it beats me why the code is not in different collors (made a copy for the forum in Arduino IDE)!? thx

/*CREATED WITH ARDUINO IDE v2.3.5 & TESTED WITH ARDUINO UNO R3 & ARDUINO NANO*/
/*Code for project with waterproof ultrasonic UART distance sensor A02YYUW and I²C 16x2 LCD display*/
/*The SoftSerial lib is used to create a second UART connection because the imbedded is used to load sketches and uses the IDE serial monitor (embedded in Arduino bootloader to communicate via the USB/UART chip on the Arduino board ). */
/*This skech is al water level meter for watertanks (or other fluids that don't damage the waterproof distance sensor). The sensor is the waterproof version of ultrasonic SR04 (sonar).
Min reading distance is 4cm, max. reading distance is 400cm. Min. & max. level kan be set in a separate diplay menu. The display shows the level in % and surface distance in cm.
There is a adjutable refill function (refill setting and stoprefill setting). The refill circuit (valve circuit) needs to be commanded by the refill output led.
A refill alarm is set at a standard (no user adjustment) time of 2 hours (7.200.000ms but can be adjusted in this sketch) and the state of the refill valve (circuit) need to be read (commanded valve state needs to = read out state).
The sensor generates many short errors in normal operation and that has been delt with in the distance() function. Only a broken sensor wire or a faulty sensor wire needs to be detected(=a certain amount of errors in a certain amount of time).
In the project circuit three leds:
-blue for power and not commanded by µController
-green led for refill ongoing
-red led (+loud high pitch buzzer) for refill alarm (valve open while AlarmTimer has counted to setting limit=alarm and/or reading 'valve open' via external readout while valve is not commanded open=alarm). Read via optocoupler at pin 5.
Two pushbuttons:
-on pin 3 for toggling the lcd screen menu's (big button prefered)
-on pin 4 for 10min alarm muting (small button prefered)
*/

//include all used libraries

#include <DistanceSensor_A02YYUW.h> //lib for ultrasonic sensor

#include <SoftwareSerial.h> //lib standard available in Arduino IDE for second UART (first UART is the embedded in the Arduino bootloader to communicatie with the USB chip on pin 0 and pin 1)

#include <LiquidCrystal_I2C.h> //lib for I²C 16x2 LCD display

#include <Wire.h> //lib standard available in Arduino IDE to enable I²C communication

#include <EEPROM.h>  //library standard available in Arduino IDE used to store the settings in the EEPROM memory


//all declarations of variables and defining of constants before void setup
//pins 0 and 1 are not used to avoid conflicts while using the serial monitor (uses the standard USB/UART connection)

#define MAINBUTTON_PIN 3  //defining main push button (Arduino)pin

#define ALARMRESET_PIN 4 //defining alarm reset button (Arduino)pin

#define VALVEREADOUT_PIN 5  //defining valve controle (Arduino)pin

#define SOFTWARE_SERIAL_PIN_RX 10 //this is where the tx (white wire) of the sensor goes (Arduino pin)!! has been changed from Arduino pin 11 to pin 10 because pin 11 is the MOSI pin from the ICSP connection used to program µC directly

#define SOFTWARE_SERIAL_PIN_TX 9 //(Arduino pin) has been changed from Arduino pin 10 to 9 because pin 10 was original pin 11 (reason above this line)

#define LED_REFILL 7  //info: the refill valve relais will be DIRECTLY COMMANDED BY THE LED PIN (optocoupler). Arduino pin!

#define LED_ALARM  8 //info: the alarm led output also needs to be connected to a high pitch buzzer (direct or indirect). Arduino pin!

//creating instances of the library objects

SoftwareSerial mySerial(SOFTWARE_SERIAL_PIN_RX, SOFTWARE_SERIAL_PIN_TX);

DistanceSensor_A02YYUW distanceSensor(&mySerial);

LiquidCrystal_I2C lcd(0x27, 16, 2); //I²C adres, 16x2 digit display

int lcdMenu = 5;  //int used to in the switch/case condition and selecting variables in the three array's. Here initial starting loop at lcdMenu 0.

char menuText[][10] = { "MinLevel", "MaxLevel", "FillStart", "FillStop", "Alarmtime" };  //this is an arry char's but these 'values' are alway the same
uint16_t menuVariables[5]; //field [0]=MinLevel; [1]=MaxLevel; [2]=FillsStart ;[3]=FillStop; [4]=Alarmtime//use of 16bit int's instead of normal(32bit) int's for minimalising EEPROM space
const uint16_t defaultVariables[5] = { 180, 30, 170, 150, 60 };  //loaded in the EEPROM at first startup (so if eeKey is not present in EEPROM)
uint8_t firmwareVersion[3] = { 1, 0, 0 };
char menuUnits[][4] = { "cm", "cm", "cm", "cm", "min" };  //3 char's + trailing 'null' character
const int eepromStart = 0;
const uint32_t eeKey = 0xDEADBEAD; //EEPROM key to flag backup creation
uint32_t readKey;
char buf[16];  //used in two void functions so global declaration

//unsigned long maxRefilltime = 5000;  //this is a variable for TESTING PURPOSE ONLY. Is used in case 1 for the alarm timer istead of menuVariables[5]
unsigned long alarmTimer;           //this variable to store the time that has passed sins the beginning of the refill command. Is limited to 72000000ms=120min=2h in case 5. unsigned long max. count is 4.294.967.295 so max time is 1.192 hours
unsigned long timeoutTime = 10000;  //When no input is given for one minute switch/case goes to case 0 and backlight goes off. Note:frist push on mainbutton 'awakes' the backlight and ables further input on the main button
unsigned long timeoutTimer;

unsigned long defaultflipTime;     //time used in lcdMenu 8 to flip lcd text on the second line
unsigned long currentdefaultTime;  //current timed time in lcdMenu 8 to flip lcd text on the second line
unsigned long defaultTime = 2000;

unsigned long SensorerrorTime = 0; //setting memory to zero. THis is a timer to check if the sensor is permanently functioning (check in the distance() function) certain amounts of errors in a certain amount of time
uint32_t SensorerrorCounter = 0;   //setting memory to zero. this is a counter to check if the sensor is permanently functioning (check in the distance() function) certain amounts of errors in a certain amount of time

unsigned int dis;  //declaring variable for the distance
float percent;
bool valveAlarmonoff = true;  //false=off, true=on

//the following int's also need to be globaly declared because they are used in different functions
unsigned int highLimit;  //counter that sets set highest level in reservoir THIS VALUE NEEDS TO BE RESTRICTED TO AT LEAST 1CM ABOVE counterMin (this means counterMin -1)
unsigned int lowLimit;   //counter that sets lowest level in reservoir

//these variables need to be set once outside the various loops @ startup. With local declaration this sketch wouldn't work.
bool risingEdge = false;  //for reading a one-time rising flank (=button returning after being pushed)
int directionInt = -1;    //needs to be int instead of boolean to increase or decrease the int counter in the dimmingCounter function
bool enterLong = LOW;
bool firstLoop = true;

bool firstFlip = true;

bool firstdefaultFlip = true;
bool defaultFlag = false;  //flag to stop the risingedgeTest from detecting while pushing button to get from lcdMenu 0 to lcdMenu 8

bool firstButtonread = LOW;
bool secondButtonread = LOW;
bool buttonState = LOW;
bool risingFlag = LOW;
bool droppingFlag = LOW;
unsigned long longTimer = 0;
bool first7passFlag = false;
bool defaulttextFlip = false;  //used in lcdMenu 8 to flip lcd text on the second line


//case 'levels' for the swich condition = easier instead of using if/else statements
//const int SCREEN_LEVEL = 5;  //tanklevel needs to be displayd in % instead of cm


void setup() {
  Serial.begin(9600);//for testing
  mySerial.begin(9600);
  lcd.init();
  //lcd.begin(); // I2C lcd dislplay
  lcd.backlight(); //I2C lcd backlight on
  lcd.clear(); //I2C clear screen
  delay(1000);                              //was here in LCD example sketch for unknown reason (waiting time for lcd to start up?)
  pinMode(MAINBUTTON_PIN, INPUT_PULLUP);    //set mainbutton as input with internal pullup
  pinMode(ALARMRESET_PIN, INPUT_PULLUP);    //set alarmresetbutton as input with internal pullup
  pinMode(VALVEREADOUT_PIN, INPUT_PULLUP);  //set valvereadout pin as input with internal pullup
  pinMode(LED_REFILL, OUTPUT);              //set led 'refill' as output
  pinMode(LED_ALARM, OUTPUT);               //set led 'alarm' as output
  alarmTimer = millis();                    //because the valveAlarmonoff is at startup true (safety first for un-mindfull users) the alarmtimer needs to be set (in the loop of case 0 impossible)

  //WRITING FIRMWARE VERSION TO EEPROM
  for (int i = 0; i < 3; i++) {
    EEPROM.put(eepromStart + sizeof(eeKey) + i, firmwareVersion[i]);  //unint8_t = one byte hence 'i' instead of 'i*2'
  }
  //GETTING THE VARIABLES FOR menuVariables WITH A FIRST STARTUP CHECK (with EEPROM eeKey check)
  EEPROM.get(eepromStart, readKey);
  if (eeKey != readKey) {
    EEPROM.put(eepromStart, eeKey);                                                                      //STORING eeKey at eeepromStart of EEPROM
    loadvarinEEPROM(); //STORING defaultVariables in EEPROM
  }
  //filling the menuVariables array with the content in the EEPROM
  for (int i = 0; i < 5; i++) {
    EEPROM.get(eepromStart + sizeof(eeKey) + sizeof(firmwareVersion) + (i * 2), menuVariables[i]);  //uint16_t 16bit = two bytes
    //Serial.println(menuVariables[i]);                                                               //for testing
  }
}

void loop() {
  switch (lcdMenu) {
    case 0:                  //SECOND DISPLAY MENU = minimum level parameter set
      if (timeoutCheck()) {  //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
        lcdMenu = 7;
        break;
      }
      lowLimit = 400;
      highLimit = menuVariables[2];  //MinLevel can't be higher than current FillStart
      longpushTest();                //longpushTest BEFORE risingEdgeTest in case the counterloop is activated to cancel (disrupt) the risingedgetest (after a long push the program needs to stay in this menu)
      risingedgeTest();
      if (risingEdge == true) {
        Serial.println((String) "risingEdge  " + risingEdge);
        resetFlags();
        lcdMenu = 1;
      } else {
        dis = distanceSensor.getDistance()/10;
        lcdFirstline();
        lcd.setCursor(0, 1);  //cursor set line 1, character 0//next line limit info
        lcd.print((String) "limits " + lowLimit + "-" + menuVariables[2] + "    ");
        //Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
        alarmCheck();
      }
      break;


    case 1:                  //THIRD DISPLAY MENU = maximum level parameter set
      if (timeoutCheck()) {  //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
        lcdMenu = 7;
        break;
      }
      lowLimit = menuVariables[3];
      highLimit = 4; //the closest measuring distance of the sensor is advised at 3cm but i'm adding 1cm to compensate for temperature fluxuations (aproximately 3%/10°C). It seems by testing 20° is the most accurate measuring temperature
      longpushTest();  //longpushTest BEFORE risingEdgeTest in case the counterloop is activated to cancel (disrupt) the risingedgetest (after a long push the program needs to stay in this menu)
      risingedgeTest();
      if (risingEdge) {
        resetFlags();
        lcdMenu = 2;
      } else {
        dis = distanceSensor.getDistance()/10;
        lcdFirstline();
        lcd.setCursor(0, 1);  //cursor set line 1, character 0//next line limit info
        lcd.print((String) "limits " + highLimit + "-" + menuVariables[3] + "    ");
        //Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]); testing
        alarmCheck();
        valveCommand();
      }
      break;

    case 2:                  //FOURTH DISPLAY MENU = refillStart level set
      if (timeoutCheck()) {  //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
        lcdMenu = 7;
        break;
      }
      lowLimit = menuVariables[0];       //the lowest refill start limit may go to MinLevel
      highLimit = menuVariables[3] + 1;  //the highest refill start limit needs to be at least 1cm under the refillStop so that there is minimum 1cm space between refillStart & refillSTop
      longpushTest();                    //longpushTest BEFORE risingEdgeTest in case the counterloop is activated to cancel (disrupt) the risingedgetest (after a long push the program needs to stay in this menu)
      risingedgeTest();
      if (risingEdge) {
        resetFlags();
        lcdMenu = 3;
      } else {
        dis = distanceSensor.getDistance()/10;
        lcdFirstline();
        lcd.setCursor(0, 1);  //cursor set line 1, character 0//next line limit info
        lcd.print((String) "limits " + menuVariables[0] + "-" + (menuVariables[3] + 1));
        //Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
        alarmCheck();
        valveCommand();
      }
      break;

    case 3:                  //FIFTH DISPLAY MENU = refillStop level set
      if (timeoutCheck()) {  //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
        lcdMenu = 7;
        break;
      }
      lowLimit = menuVariables[2] - 1;  //the lowest refill stop limit needs to be at least 1cm above the RefillStart
      highLimit = menuVariables[1];     //the highest refill stop limit by MaxLevel
      longpushTest();                   //longpushTest BEFORE risingedgeTest in case of entering the counterloop to cancel (disrupt) the risingedgeTest (after a long push the program needs to stay in this menu)
      risingedgeTest();
      if (risingEdge) {
        resetFlags();
        lcdMenu = 4;
      } else {
        dis = distanceSensor.getDistance()/10;
        lcdFirstline();
        lcd.setCursor(0, 1);  //cursor set line 1, character 0//next line limit info
        lcd.print((String) "limits " + (menuVariables[2] - 1) + "-" + menuVariables[1]);
        //Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
        alarmCheck();
        valveCommand();
      }
      break;

    case 4:                  //SIXTH DISPLAY MENU = alarm timer set//this is a temporary menu because the timmer check for abnormal long filling time needs to be done independent from the Arduino
      if (timeoutCheck()) {  //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
        lcdMenu = 7;
        break;
      }
      lowLimit = 120;  //120min in case 5 'lowLimit' is misleading but this needs to be like this because case 5 uses the same functions as case 1,2,3 & 4
      highLimit = 1;   //1min in case 5 highLimit is misleading...
      longpushTest();  //longpushTest BEFORE risingEdgeTest in case the counterloop is activated to cancel (disrupt) the risingedgetest (after a long push the program needs to stay in this menu)
      risingedgeTest();
      if (risingEdge) {
        resetFlags();
        lcdMenu = 6;
      } else {
        dis = distanceSensor.getDistance()/10;
        lcdFirstline();
        lcd.setCursor(0, 1);  //cursor set line 1, character 0//next line limit info
        lcd.print("limits 1-120min");
        //Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
        alarmCheck();
        valveCommand();
      }
      break;

    case 5:                  //FIRST DISPLAY MENU = distance/level in percentage
      if (timeoutCheck()) {  //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
        lcdMenu = 7;
        break;
      }
      longpushTest();  //in longpushTest if lcdMenu 0 then make lcdMenu 8 active
      risingedgeTest();
      if (risingEdge == true) {
        //Serial.println((String) "risingEdge  " + risingEdge);
        resetFlags();
        lcdMenu = 0;
      } else {
        //dis = 160;  //this line for testing puspose instead of the next line
        //dis = sr04.Distance();//read distance
        distance();
        percent = ((menuVariables[0] - (float)dis) / (menuVariables[0] - menuVariables[1])) * 100; //percentage//dis needs to be float so a float can be devided by a float, otherwise warning that result might be wrong
        lcd.setCursor(0, 0);
        lcd.print((String) "tanklevel " + (int)percent + "%   "); //with two extra spaces after % to clear the screen from very 120% reading at startup
        lcd.setCursor(0, 1);
        lcd.print((String) "distance " + dis + "cm    ");//with three spaces after cm to clear the screen
        alarmCheck();
        valveCommand();
      }
      break;

    case 6:                  //SEVENTH DISPLAY MENU = alarm on/off set (off if there is no refill valve used)
                             //LongpushTest BEFORE risingEdgeTest in case the counterloop is activated to cancel (disrupt) the risingedgetest (after a long push the program needs to stay in this menu)
                             //There is in the longpushTest a if-statement branch to the flipSwitch function instead of the counterloop
      if (timeoutCheck()) {  //if timeoutCheck returns true then go to timeout 'ghost menu' (=case 7)
        lcdMenu = 7;
        break;
      }
      longpushTest();
      risingedgeTest();
      if (risingEdge) {
        resetFlags();
        lcdMenu = 5;
      } else {
        dis = distanceSensor.getDistance()/10;
        displayAlarmstate();
        alarmCheck();
        valveCommand();
      }
      break;

    case 7:  // EIGHT DISPLAY MENU = this is a 'ghost timeout menu' that keeps on watching parameters but shuts down LCD (backlight=I2C version, only text non-I2C version) and waits for the main button
             //to be pushed to re-light the LCD backlight and go menu 0. When a timeout occures (mainbutton not pressed) in one of the other CASEs/Menu's
             //the menu get's set to this CASE 7.
      char fields[3][7]; //declare a char type variable with 7 character size in an array of 3.
      alarmCheck();
      if (digitalRead(LED_ALARM) == 1) {
        if (valveAlarmonoff == true && (millis() - alarmTimer) > (menuVariables[4] * 60000)){
          strncpy(fields[0], "VlvTi ", 7); //Valve Timer
        }else {
          strncpy(fields[0], "      ", 7);
        };
        if (valveAlarmonoff == true && (digitalRead(VALVEREADOUT_PIN) == 0 && digitalRead(LED_REFILL) == 0)){
          strncpy(fields[1], "Vlv ", 7); //Valve (open or closed)
        } else {
          strncpy(fields[1], "    ", 7);
        };
        if ((menuVariables[0] < dis) || (dis < menuVariables[1])){ 
          strncpy(fields[2], "TnkLLi", 7); //Tank Level Limit
        } else {
          strncpy(fields[2], "      ", 7);
        };
        lcd.backlight();//I2C backlight on
        lcd.display(); //I2C display lighted
        lcd.setCursor(0, 0);  //cursor set line 1, character 0//next line limit info
        lcd.print(" ALARM DETAILS  ");
        lcd.setCursor(0, 1);  //cursor set line 1, character 0//next line limit info
        lcd.print((String)fields[0] + fields[1] + fields[2]);       
       } else {
        lcd.clear();      //I2C lcd clear
        lcd.noDisplay();  //I2C display no dots lighted
        lcd.noBacklight(); //I2C backlight off     
      };
      risingedgeTest(); //on pushing button go to menu case 5
      if (risingEdge) {
        resetFlags();
        lcd.clear(); //I2C clear screen
        lcd.backlight();//I2C backlight on
        lcd.display(); //I2C display lighted
        lcdMenu = 5;
      } else {
        distance();
        alarmCheck();
        valveCommand();
      };
      break;

    case 8:  //NINTH MENU = THE 'RESET TO DEFAULT' MENU. THIS ONE IS ONLY ACCESABLE IN lcdMenu case 5 VIA THE LONGPUSHTEST
      //NO TIMEOUT CHECK IN THIS MENU

      if (defaultFlag == false) {
        if (digitalRead(MAINBUTTON_PIN) == 1) {  //if the pushbutton is not pushed next risingedgeTest needs to be disrupted otherwise the droppingFlag stays true from the last 'loop'
          droppingFlag = false; //disrupting next risingedgeTest
          defaultFlag = true;  //after setting defaultFlag to 'true' this will change ONLY at EXIT lcdMenu 8 (so after risingedge true)
          //Serial.println((String)"defaultFlag " + defaultFlag); //testing
        }
      }
      if (defaultFlag) {  //if the pushbutton is not pressed any more after switching from lcdMenu 0 to lcdMenu 8, pushbutton is ready to be used in risingedgeTest
        longpushTest();
        risingedgeTest();
      }
      if (risingEdge) {
        //Serial.println((String) "risingEdge occured");                        //testing
        defaultFlag = false;                                                  //with exit lcdMenu 8 reset condition for the next time lcdMenu 8 is entered
        //Serial.println((String) "defaultFlag at risingEdge " + defaultFlag);  //testing
        resetFlags();                                                         //needs to be here a otherwise after exeting lcdMenu 8 a immediate dorpppongFlag and risingedgeFlag are triggered
        firstdefaultFlip = !firstdefaultFlip;                                 //resetting so when entering lcdMenu 8 the next time defaultflipTime will be resetted to millis()
        lcd.clear();
        lcdMenu = 5;
      } else {
        dis = distanceSensor.getDistance()/10;

        if (firstdefaultFlip) {
          lcd.clear();
          lcd.setCursor(0, 0);  //cursor set line 1, character 0//next line limit info
          lcd.print("TO DEFAULTS?     ");
          //Serial.println((String) "first defaulttextFlip: " + defaulttextFlip);  //testing
          defaultflipTime = millis();
          firstdefaultFlip = !firstdefaultFlip;
        }
        currentdefaultTime = millis();
        if (defaulttextFlip == false && (currentdefaultTime - defaultflipTime) > defaultTime) {  //time between changing text on second line 2 seconds
          //Serial.println("entering LONG");                                                       //testing
          lcd.setCursor(0, 1);                                                                   //cursor set line 1, character 0//next line limit info
          lcd.print("LONGPUSH=CONFIRM");
          Serial.println((String) "LONGdefaulttextFlip: " + defaulttextFlip);  //testing
          defaulttextFlip = !defaulttextFlip;                                  //next cycle other text line
          //Serial.println((String) "LONGdefaulttextFlip: " + defaulttextFlip);  //testing
          //Serial.println(defaultflipTime);                                     //testing
          defaultflipTime = millis();                                          //setting timer to zero
          //Serial.println(defaultflipTime);                                     //testing
        }
        currentdefaultTime = millis();
        if (defaulttextFlip == true && (currentdefaultTime - defaultflipTime) > defaultTime) {  //time between changing text on second line 2 seconds
          Serial.println("entering SHORT");                                                     //testing
          lcd.setCursor(0, 1);                                                                  //cursor set line 1, character 0//next line limit info
          lcd.print("SHORTPUSH=EXIT  ");
          //Serial.println((String) "SHORTdefaulttextFlip: " + defaulttextFlip);  //testing
          defaulttextFlip = !defaulttextFlip;                                   //next cycle other text line
          //Serial.println((String) "SHORTdefaulttextFlip: " + defaulttextFlip);  //testing
          defaultflipTime = millis();                                           //setting timer to zero
        }
        //Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
        alarmCheck();
      }
      break;
  }
}


void distance() {      //aside of measuring this function needs to detect a certain amount of sensor not-ok readings in a certain time to keep looping with "SENSOR ERROR" on the display
  DistanceSensor_A02YYUW_MEASSUREMENT_STATUS meassurementStatus;
  unsigned long Millis;   //declaring localy because only used in this function
  do {
    meassurementStatus = distanceSensor.meassure(); 
    if (meassurementStatus == DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK) {
      dis = distanceSensor.getDistance()/10; //divided to convert to cm
      SensorerrorCounter = 0; //test with no effect
      Serial.println((String) "reading normal distance=sensor OK");
    } else {                      //this else code is here in case the sensor is dead or a sensor wire is broken
      Serial.println((String) "entering sensorerror else condition=sensor NOT OK"); //for testing
      Millis = millis();
      ++SensorerrorCounter;
      Serial.println(SensorerrorCounter);//testing
      Serial.println(Millis);            //testing
      Serial.println(SensorerrorTime);   //testing
      if ((Millis - SensorerrorTime) > 5000 && SensorerrorCounter > 4) {     //if there are more then 4 sensor errors in 5 sec(5000ms) then the "SENSOR ERROR" message needs to be displayed!!attention shis needs to be 5sec and 4 errors because the µC is to slow (long cycles)                        
           lcd.clear();
           lcd.setCursor(0, 0);  //cursor set line 1, character 0//next line limit info
           lcd.backlight();
           lcd.print("SENSOR ERROR   ");
           Serial.println((String) "conditions SensorerrorTime and SensorerrorCounter");//testing
           SensorerrorTime = millis(); //reset the 1 sec timer to do a new cycle of 1sec to check if sensor doensn't generate a not-ok status
           SensorerrorCounter = 0; //reset counter together with a new 1sec cycle
      } else {
        if ((Millis - SensorerrorTime) > 5000) {
           SensorerrorTime = millis(); //reset the 1 sec timer
           SensorerrorCounter = 0;
           Serial.println((String) "condition SensorerrorTime only");//for testing
        }   
        }    
      }
  }  while (meassurementStatus != DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK); //repeats the code between do and while untill DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK is true
}

/*void distance() {
  DistanceSensor_A02YYUW_MEASSUREMENT_STATUS meassurementStatus;
  meassurementStatus = distanceSensor.meassure();
  while (meassurementStatus != DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK) {
    meassurementStatus = distanceSensor.meassure();
    if (meassurementStatus == DistanceSensor_A02YYUW_MEASSUREMENT_STATUS_OK) {
      dis = distanceSensor.getDistance()/10;
    };
  }
}*/

void longpushTest() {
  unsigned long delayTime = 1000;
  if (!digitalRead(MAINBUTTON_PIN)) {  //if the button is pushed then go to timercheck
    if (firstLoop == true) {
      longTimer = millis();
      firstLoop = false;
      firstFlip = true;  //resetting the condition to 'flip' one time while pushing long BEFORE entering the next if statement
      //Serial.println((String) "longTimer  " + longTimer + "second");
      //if (lcdMenu >= 1 && lcdMenu <= 5) {
      //Serial.println(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
      //};
    }
    if ((millis() - longTimer) > delayTime) {  //timercheck: if the difference reaches 1000ms it is bigger than the delaytime the counterLoop wil be called
      //Serial.println((String) "entering counterLoop or flipSwitch"); testing
      if (lcdMenu == 6) {
        //Serial.println((String) "entering flipSwitch"); testing
        flipSwitch();
      } else if (lcdMenu == 8) {                                                                               //once in the default setting menu (after 'if (defaultFlag)'!) going to default settings with a longpush
          loadvarinEEPROM(); //STORING defaultVariables in EEPROM
          size_t numBytes = sizeof(defaultVariables);
          memcpy(menuVariables, defaultVariables, numBytes );//copying default const values to the variables that can be changed in running time         
          lcd.clear();
          lcd.setCursor(0, 0);                          //cursor set line , character 0//next line limit info
          lcd.print(" DEFAULT READY ");
          delay(3000); //freezing all activity 3sec with message the device is set to default
      } else if (lcdMenu == 5) {  //going from lcdMenu (display tanklevel) to default setting menu with a longPush
        lcdMenu = 8;
      } else { //if ((lcdMenu != 6) && (lcdMenu != 0)) {
          //Serial.println((String) "entering counterLoop");//testing
          counterLoop(); // AS LONG AS THE BUTTON IS BEING PUSHED THE SKETCH KEEPS 'LOOPING' IN THE WHILE LOOP OF THE COUNTERLOOP FUNCTION
          droppingFlag = false; //disrupting the risingedgeTest after exiting the the 'long push' in the longpushtest                                                                                                      //increase counter as long as button is pushed (=low)
          EEPROM.put(eepromStart + sizeof(eeKey) + sizeof(firmwareVersion) + ((lcdMenu) * 2), menuVariables[lcdMenu]);  //saving the altered variable in the EEPROM without comparison because after certainty of change
      };
    }
      //Serial.println((String) "BUTTON PRESSED  " + (millis() - longTimer) + "ms");
  } else if (digitalRead(MAINBUTTON_PIN)) {  //if button is not pushed (=input high) reset the timer by forcing longTimer = millis() the next cycle
    firstLoop = true;
  }   
}

  void risingedgeTest() {                                     //it is no use to make this a bool fucntion. although the value can be directly returned to the calling function the parameters have to be reset after every jump to the next lcdMenu so these need to be declared globaly (so risingEdge is also globaly declared)
    firstButtonread = digitalRead(MAINBUTTON_PIN);            //reading button first time. There is no need compensate jitter in running time with 'millis()' because this takes more extra processing time (aprox 12ms and minimum 5 extra variables) and the delay(10) can't cause failures or unresponsive main pushbutton in this sketch
    delay(10);                                                //two readouts with a small delay to compensate jitter advised 50ms but 10ms works better and with a 68nF capacitor parallel in button works even better.
    secondButtonread = digitalRead(MAINBUTTON_PIN);           //reading button second time
    if (firstButtonread == LOW && secondButtonread == LOW) {  //LOW/LOW = no jitter
      buttonState = LOW;                                      //=LOW means the button is pushed!! input pulling to GND!
      timeoutTimer = millis();                                //by each press on the main button resetting the timer for the timeout. NEEDS TO BE IN RISINGEDGETEST BECAUSE RISINGEDGETEST IS THE ONLY FUCTION THAT IS CALLED IN ALL THE CASSES
    }
    if (firstButtonread == HIGH || secondButtonread == HIGH) {  //one of the two HIGH means jitter or button just released
      buttonState = HIGH;
    }
    if (buttonState == LOW) {  //testing if a dropping edge has occured
      droppingFlag = HIGH;     //the droppingEdge needs to be flaged if occurring
      //Serial.println((String) "droppingFlag  " + droppingFlag); //for testing
    }  //when the button is continuous pushed this droppingFlag STAYS HIGH!
    //if droppingFlag is high, testing if a risingEdge is occuring in the next 'loops'(droppingFlag stays HIGH no matter what!)
    if (droppingFlag == HIGH) {
      if (firstButtonread == HIGH && secondButtonread == HIGH) {
        buttonState = HIGH;
      };
      if (firstButtonread == LOW || secondButtonread == LOW) {
        buttonState = LOW;
      }
      if (buttonState == HIGH) {
        risingFlag = HIGH;
        //Serial.println((String) "risingFlag    " + risingFlag + "   ");
      }
      //if the butten has been pushed (=droppingFLag) en some cycles later been pushed (=risingFlag) the condition for toggle to the next menu is ok
      if (droppingFlag == HIGH && risingFlag == HIGH) {
        risingEdge = true;
      } else {
        risingEdge = false;
      }
    }
  }

  void counterLoop() {
    int Speed = 0;
    int counterSpeed = 1;
    while (!digitalRead(MAINBUTTON_PIN)) {
      //Serial.println("COUNTERLOOP"); // testing purpose
      //Serial.println((String) "MinLevel " + counter + " cm    inside while loop"); // testing
      valueCounter();  //increase or dercease counter one step
//DISPLAYING values of current lcdMenu 'while' adjusting limits up or down
      sprintf(buf, "%-10s%3d%-3s", menuText[lcdMenu], menuVariables[lcdMenu], menuUnits[lcdMenu]); //put everything as a string in a buffer buf
      lcd.setCursor(0, 0);
      lcd.print(buf);
      if (menuVariables[lcdMenu] == highLimit || menuVariables[lcdMenu] == lowLimit) {
        lcd.setCursor(0, 1);  //cursor set line 1, character 0//
        lcd.print("      !LIMIT!       ");
      }
      enterLong = HIGH;  //setting variable to know if the while loop has been entered
      //the next six lines adjust the toggle speed in setting a value 'while' pushing the button longer then one seconde
      if (counterSpeed >= 1 && counterSpeed < 6) {
        Speed = 400;  //250ms
      } else if (counterSpeed >= 6 && counterSpeed < 15) {
        Speed = 200;  //125ms
      } else if (counterSpeed == 15) {
        Speed = 50;  //50ms
      };
      delay(Speed);  //the toggle speed on the counter/display value. This needs to changed to 3 automatic speed levels for user friendly use
      if (counterSpeed >= 1 && counterSpeed <= 15) {
        counterSpeed++;
      };
      //Serial.println((String) "counter  " + counter + "   directionInt  " + directionInt); //testing purpose
    };
    if (enterLong == HIGH) {         //enterLong is used to make the counterdirection change AFTER from the moment the button is released
      directionInt = -directionInt;  //flip the sign/counter direction after 1sec push
      enterLong = LOW;               //re-setting to LOW so the next time directioInt can be fliped in case of going tru while loop
      //Serial.println((String) "directionInt  " + directionInt); //testing purpose
    };
  }

  //void fuction because it does not need to return a value. int counter is declared globaly
  void valueCounter() {
    if (menuVariables[lcdMenu] > highLimit && menuVariables[lcdMenu] < lowLimit) {  //THE LIMIT VALUES NEED TO BE CHANGED ACCORDING TO THE MENU!!!SO THESE VALUES (2 & 401) ARE FOR MENU 2 ONLY
      menuVariables[lcdMenu] = menuVariables[lcdMenu] - directionInt;
    }
    //the next two if functions are used to 'push' the counter in one desired direction
    //after the button has been released on highLimit or lowLimit and re-pressed; longer than a second
    //in order to get in to the if function above. So it's a special method so the counter doens't get 'outside the limits hense blocking the function above
    if (menuVariables[lcdMenu] == highLimit && enterLong == LOW) {
      menuVariables[lcdMenu] = menuVariables[lcdMenu] + 1;
    }
    if (menuVariables[lcdMenu] == lowLimit && enterLong == LOW) {
      menuVariables[lcdMenu] = menuVariables[lcdMenu] - 1;
    }
    //Serial.println((String) "directionInt  " + directionInt); // testing
  }

  void valveCommand(){
            if (dis >= menuVariables[2] && dis > menuVariables[3]) {
          digitalWrite(LED_REFILL, 1);
        } else {
          digitalWrite(LED_REFILL, 0);
        };
  }

  void resetFlags() {
    droppingFlag = LOW;  //resetting buttonreading function of rising edge after led switch
    risingFlag = LOW;    //resetting buttonreading of rising edge after led switch
    risingEdge = false;  //resetting buttonreading of rising edge after led switch
  }

  void flipSwitch() {                       //instead of a counterLoop is the function to set the alarm active (on) or inactive and is entered in the longpushTest function
    while (!digitalRead(MAINBUTTON_PIN)) {  //after experimenting there is a while loop implemented because the droppingEdge kempt going back up with random results at pushbuttonrelease
      if (firstFlip == true) {
        valveAlarmonoff = !valveAlarmonoff;
        displayAlarmstate();
        if (valveAlarmonoff == true) {
          alarmTimer = millis();
          //Serial.println("    alarm on in flipswitch"); // testing
        };
        //if (valveAlarmonoff == false) {
        //Serial.println("    alarm off in flipswitch");// testing
        //};
        firstFlip = false;  //making shure when the butting is still pushed in the second pass true the flipSwitch function that valveAlarmonoff stays the same
        //Serial.println((String) "firstFlip" + firstFlip);//for testing
        //} else {
        //Serial.println((String) "    alarm " + valveAlarmonoff + " in flipSwitch Whileloop");// testing
      };
    };
    droppingFlag = false; //disrupting next risingedgeTest when the buttom is nog pushed anymore
  }

  //these are the two alarm checks. One for detection exceeding refill time (menuVariables[4]) and one if the valve is still open while the rifill command has stoped
  //first one timecheck
  void alarmCheck() {
    //Serial.println((String) alarmTimer);//for testing
    
//when alarm is on: check the refill timer, check if the valve is closed (VALVEREADOUT_PIN) while not commanded open (LED_REFILL, this is reading a output!!but should work) and check if tank is not to 'empty' or to 'full'
//if ((millis() - alarmTimer) > maxRefilltime || digitalRead(VALVEREADOUT_PIN) == digitalRead(LED_REFILL)){ //this line is for testing purpose only. Here you can shorten the time to a few seconds instead of a one minute minimum
    if ((valveAlarmonoff == true && ((millis() - alarmTimer) > (menuVariables[4] * 60000) || (digitalRead(VALVEREADOUT_PIN) == 0 && digitalRead(LED_REFILL) == 0))) || ((menuVariables[0] < dis) || (dis < menuVariables[1]))) {  //menuVariables[4] is a value that represents minutes hense *60000 to convert to ms
      digitalWrite(LED_ALARM, 1);
    } else {
      digitalWrite(LED_ALARM, 0);
    };
    //the next is a buttonread to reset the alarm TIMER. This means the alarm timer detection is only 'delayed' but the alarms (timer and 'valve open while not commanded') stay ON.
    //note!: the button readout is done with a simple anti-jitter delay instead of the millis() readout methode because in this case some ms extra
    //processing time is not a isseu AND: the millis() readout methode also takes a minimum of 12ms extra and at least five extra unsigned long values!!
    if ((valveAlarmonoff == true) && (millis() - alarmTimer) > (menuVariables[4] * 60000)) { //only reset timer if valveAlarmonoff is on and timer alarm has reached limit
      if (!digitalRead(ALARMRESET_PIN)) {  //using analog pin A4 as digital input with internal pullup
        delay(10);
        if (!digitalRead(ALARMRESET_PIN)) {
          //Serial.println((String) alarmTimer + "  before alarmTimer()=millis()"); //for testing
          alarmTimer = millis();  //if alarmresetbutton is pushed, reset the alarmtimer
          //Serial.println((String) alarmTimer + "  after alarmTimer()=millis()");//for testing
        };
      };
      //if (valveAlarmonoff == false) {
        //digitalWrite(LED_ALARM, 0);
      //};
    };
  }

  void lcdFirstline() {
    sprintf(buf, "%-10s%3d%-3s", menuText[lcdMenu], menuVariables[lcdMenu], menuUnits[lcdMenu]);  //put the array's of chars as a string in the buffer buf
    lcd.setCursor(0, 0);
    lcd.print(buf);
  }

  void displayAlarmstate() {
    if (valveAlarmonoff == false) {
      lcd.setCursor(0, 1);                //cursor set line 1, character 0
      lcd.print("                    ");  //erasing the second line the previous (16 characters) on the lcd display
      lcd.setCursor(0, 0);
      lcd.print("Alarm off       ");
      //Serial.println((String) "    alarm off    ");
    } else if (valveAlarmonoff == true) {
      lcd.setCursor(0, 1);                //cursor set line 1, character 0
      lcd.print("                    ");  //erasing the second line the previous (16 characters) on the lcd display
      lcd.setCursor(0, 0);
      lcd.print("Alarm on        ");
      //Serial.println((String) "    alarm on     ");
    }
  }

  bool timeoutCheck() {
    if ((millis() - timeoutTimer) > timeoutTime) {
      first7passFlag = false;
      return true;
    } else {
      return false;
    };
  }

  void loadvarinEEPROM(){
      for (int i = 0; i < 5; i++) {                                                                        //STORING defaultVariables in EEPROM
      EEPROM.put(eepromStart + sizeof(eeKey) + sizeof(firmwareVersion) + (i * 2), defaultVariables[i]);  //uint16_t 16bit = two bytes
      }
  };

…i not a big specialist but is C++ actualy that pogrammer unfriendly that i’m not able to use a simple > operator between a subtraction of two usigned long ‘s and a direct constant value (5000)? ….i’m a year at it since i started with Arduino C++ and that language imo realy sucks. I get it; C++; because one needs the nuances in the language to write hardware specific code….but C++ has evolved in to a ugly big monster if you ask me. Besides writing the code is a puzzle, using the right syntax is a puzzle to.

So with that out of my system.
Thank you for the tip! I’ll try it asap

I have been a programmer for more than 50 years, and when I first heard about C++, I said no thanks. The benefits of C++ I got from the subset G compilers I used, and I couldn't see a practical use for anything beyond that. I feel the same about python but may be forced to learn it for the UNO Q.

It's not just the constant 5000; you also need to be careful about the order of operations. It is a well-known problem where things get added and overflow; run any math like that through the debugger on your shoulders. If in doubt, do not be afraid to use multiple statements, doing a small, simple step at a time. Eventually, you will learn how to make the code more efficient without breaking it.

I'm not one to jump on the "Evils of Strings" bandwagon. But, you're using String objects a lot (and in most cases completely unnecessarily) on an AVR processor with only 2K of RAM. Not saying it is, but it could be a memory problem.

1 Like

Without a hint as to where this library might be found, no one can compile this but you. That is a problem.

And yes, this is not necessary(grabbed at random):
Serial.println((String) " alarm on ");

try a simple
Serial.println(F(" alarm on "));

which places the unchanging string in program memory, to boot.

There are many, many optimizations to be made, but until we can compile, we'll simply observe. Optimized code has far better chance of making bad assumptions, and bad code, visible.

1 Like

I'm not quite blind after perusing your function. But I have to stop now.

It's not at all clear what you are aiming for. It looks like either spacing up to four attempts by five seconds, or allowing five seconds total for up to four errors.

I believe it does neither. There is no mechanism for pacing the attempts. Nothing in the function blocks; attempts are made as fast as possible and the function can only exit if the success criterion is met.

This should be changed to a function that does not have a while or do / while loop.That function should be called every five seconds, and the caller should count those calls for too many and fail, or see a call succeed.

a7

1 Like

As far as I know syntax highlighting on the forum has its limits.

  • If the code exceeds a certain length syntax highlighting will be skipped.
  • Reason (to my knowledge) is that it's time consuming.
2 Likes

ROFL!! You don’t have to buy the newest model car do you? It doesn’t matter how it evolved. It’s roots are in c (Code for latin) You can write in any subset of c++ you want. I typically write in some version from the late ‘90s. Alway’s have, probably always will. You can cherry pick whatever looks tasty from the new stuff if you like.

-jim lee

1 Like

Well thats the f*cking problem for noobs like me (yes after a year i start to get real frustrated). Always tips to improvements (mostley without an explanation why) but the code i wrote i got to work very hard fought! It’s difficult to get some working info (always to basic for my purpuses). The code that you guy’s use is for me sometimes impossible to figure out…so i try many things and hang on to what works (without knowing if it is the most efficient solution).

So thanks for the tip, i’ll look in to it but for now i don’t know what the real difference is. I don’t know in detail what the compiler and the uC does….and that is stuff that doesn’t get a descent and comprehensible explanation anywhere.

Yeah right….and all the info i find as a noob use all the itterations. What do i know about those amended code over the years. It’s difficult enough to figure it al out and i don’t have a degree in computerscience. So yes it’s a monster to me

This is a problem because buf is too small. 10 characters (%-10s) plus 3 characters (%3d) plus 3 characters (%-3s); that is 16 characters excluding the terminating null character.

This can hold 16 characters (including the terminating null character). You did not cater for the terminating null character; buf should be 17 characters.

No, that will work fine. The usual probably is trying to do stuff like delay(100*1000); // delay for 100 seconds where both numbers are treated as 16bit, and the product doesn't fit in 16bits, so it produces unexpected results (delays for about 34s!) This is behavior inherited from C, and not a C++ thing.
It rarely comes up on non-microcontrollers, because constants usually default to 32bits (but only 16b on an AVR.)

BTW, I really like the comment at the start of your program, aside from the lines being too long... I wish more code posted here had a simple explanation of approximately what it's supposed to do at the top!

(I haven't looked at the code much beyond that, since the other suggestions about removing String and fixing buf seem like important first steps.)

1 Like

This one

Thx! At least that is ok.

It seems to work but i’ll look in to it. Thx

I turned it inside out. This function blocks for five seconds if slow failed attempts less than four occur, or returns immediately after four failed attempts that happen faster.

bool try()
{
  int fails = 0;
  unsigned long start = millis();

  while (millis() - start < 5000) {
  
    bool success = someProcess();   // true is success, this is an attempt

    if (success) return true;       // we done, happy

    fails++;
    if (fails >= 4) return false;   // we outta here, too many attempts
  }
  
  return false;   // ran out of time
}

Early returns in functions don't offend me, especially in simple functions like this. if they offend you, deal with it in the usual manner.

I have not tested this, but it is the kind of thing I tend to get right first time. I will test it when I get to the lab.

a7