Loop state logic

I am using and ESP32 and this is my timer cycle loop code. i tried to comment in the variables how it works the best i can. this code is part of a much much larger program. will someone please check my logic. i commented the order and logic of things. im trying to track a bug i cant identify. does anyone see anything that might not be doing what i think.

I have been working at this code for 2 days now and im stumped. please have a look thankyou.

if (_EEPROM.timerMode == 3) {
    if (millis() - startupdelayMillis >= timerMode3StartupDelay) {
      if (_EEPROM.enabled) {
Serial.println("tmode=3");
        if (secondstime - _EEPROM.timerMode3LastCycletime > _EEPROM.frequency && timerMode3State == 0) {
          if (fullFloatSensorStatus) { //if is FULL continue
            flowMilliLitres = 0; //reset MilliLitres
            totalMilliLitres = 0; //reset totalMilliLitres
            handleTimers(1, 1, 0, 1); //turn on relay 3 "dosePump on"
            timerStates[2] = 1; //timer 3 state 1
            _EEPROM.timerRuntimes[2]++; // run counter
            timerMode3State = 1; // go to next state
            _EEPROM.timerMode3LastCycletime = secondstime; // timerMode3LastCycletime rtc.epoch Seconds
            _writeEEPROM(); //set timerMode3LastCycletime new value to eeprom memory

          }
        }
        if (timerMode3State == 1) {
          if (millis() - previousMillis > interval) {
            pulse1Sec = pulseCount;
            pulseCount = 0;
            flowRate = ((1000.0 / (millis() - previousMillis)) * pulse1Sec) / _EEPROM.calibrationFactor;
            previousMillis = millis();
            flowMilliLitres = (flowRate / 60) * 1000;
            flowLitres = (flowRate / 60);
            totalMilliLitres += flowMilliLitres;
            totalLitres += flowLitres;
          }
          if (totalMilliLitres >= _EEPROM.timerMode3MaxML && doseUsed) {
            handleTimers(1, 1, 1, 0); //turn off dose pump turn on mixing pump
            timerStates[2] = 0; //timer 3 state 0 for off
            timerStates[3] = 1; //timer 4 state 1 "mixing pump is on"
            _EEPROM.timerRuntimes[3]++;
            doseUsed = false; // new dose not used yet
            Mix = true; // enable mix state true
            prevMixmillis = millis(); // set mix prevmillis
          }

          if (Mix && millis() - prevMixmillis >= 20000) { //wait for mix to be done enable feed pump
            readyToFeed = true; //enable feed pump power by setting readytofeed true
            prevMixmillis = millis();
          }


          //if tank not empty and has dosed enough ml and is ready to feed, then power on feed pump
          if (!emptyFloatSensorStatus && totalMilliLitres >= _EEPROM.timerMode3MaxML && readyToFeed) {
            totalMilliLitres = 0; // reset to 0 for next dose cycle
            doseUsed = true; // prev dose has been used so doseUsed true
            readyToFeed = false; // not ready to feed again yet
            handleTimers(1, 0, 1, 1);//turn off mixing pump and turn on feed pump 
            timerMode3State = 2; // next cycle state enable feed pump power off countdown
            timerStates[1] = 1; // feed pump power state is true
            timerStates[3] = 0; //mixing pump state if off
            _EEPROM.timerRuntimes[1]++; // feed pump counter

          }
        }
        if (timerMode3State == 2) {
          if (emptyFloatSensorStatus) { // wait until tank empty
            handleTimers(1, 1, 1, 1); // turn off all relays/pumps
            timerStates[1] = 0; // feed pump state is off
            _EEPROM.timerMode3LastCycletime = secondstime;// update eeprom variable for beginning of new cycle 
            timerMode3State = 0; // set timerMode3State to 0 to restart at beginning.
          }
        }
      }
    }
  }

i update secondstime with eopch seconds from rtc time chip.

in timerMode3State 0 after timeout has expired turn on dose pump if tank is full. then timer3ModeState 1 will wait until flow meter counts set milliliters. after mL is dosed then we start mixing pump for 20 seconds, then we start feed pump and repeat.

I understand there is some guess work for the reader without the complete code. "assuming" the timer/time variables that you cant see are correct, how about the flow of the state change. does it do what i described or is something out of place?

Recently during testing, sometimes the mixpump don't run. so im thinking maybe one of the states is out of order but it looks right to me. maybe someone else will notice a problem i have not recognized yet.

When i powercycled the mcu it worked fine. but it seemed like after the 72000 second"_EEPROM.frequency" duration expired the mix pump didn't run before starting the feed pump.

Are the variables holding the millis(), especially with a Uno/Mega, declared as UL or ints?

This is my main sketch. i have lots of .h files with the code sepperated to organize things a little bit. processTimers() is the function that my timer code is in.

Anyways yes i think i declared them right. I m sorry sorry i failed to mention i am using an ESP32 as MCU.

#include <Wire.h> //I2C library
#include "RTClib.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>
#include <EEPROM.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include <SPIFFS.h>
#include <ElegantOTA.h>

#define flowSENSOR  18

char nodeID0[] = "NODTM1";
char nodeID1[] = "TM1ODE";
char nodePairID0[] = "TM1PAI";
char nodePairID1[] = "IAP1MT";


WebServer update_server(81);
WebServer server(80);

IPAddress local_IP(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);

WiFiUDP Udp;
RTC_DS3231 rtc;

unsigned long timerMode3StartupDelay = 30000;

unsigned int ServerPort = 4210;
unsigned int localUdpPort = 4220;
bool startupdelay = true;
unsigned long startupdelaymillis = 0;
const char* ssid = nodeID0;
const char* password = "a1b2c3d5";
bool pairMode = false;
bool adminEnabled = false;
bool apMode = false;
unsigned long apTimeout = 15000;
unsigned long apSwitchMillis = 0;
bool apState = false;
unsigned long lcdMillis = 0;
int photoSensor = 0;
char incomingPacket[255];// buffer for incoming packets
int addr = 0;
int timerMode3State = 0;
bool writeEEPROM = false;
int timerStates[4] {0, 0, 0, 0};
int photoSensorpin = 14;
byte fullFloatSensor = 34;// float sensor1 pin
byte emptyFloatSensor = 27;// float sensoqr2 pin
const int relay[] = {26, 25, 33, 32};
int timerState = 1;
bool lightsOn = 1 ;
bool doseUsed = true;
bool readyToFeed = false;
bool Mix = false;
bool fullFloatSensorStatus;
bool emptyFloatSensorStatus;

unsigned long now7 = 0;
unsigned long now2 = 0;
unsigned long now4 = 0;
unsigned long now5 = 0;
unsigned long now0 = 0;
unsigned long now1 = 0;
unsigned long now10 = 0;
unsigned long timer1onMillis = 0;
unsigned long timer2onMillis = 0;
unsigned long timer3onMillis = 0;
unsigned long timer4onMillis = 0;
unsigned long timer1offMillis = 0;
unsigned long timer2offMillis = 0;
unsigned long timer3offMillis = 0;
unsigned long timer4offMillis = 0;
unsigned long prevMixmillis = 0;
unsigned long secondstime = 0;
unsigned long startupdelayMillis = 0;
char str[20]; // datetime object

struct {
  int timerMode;
  char masterIP[20];
  bool timerEnabled[4];
  unsigned long timerRuntimes[4];
  bool timerRunning[4];
  unsigned long timerTimes[4][4];//1-4 on,off,onN,offN
  unsigned long frequency = 72000;//timer mode 3 timer frequency
  unsigned long timerMode3WaterSolenoidDuration; //duration to activate water solenoid in seconds
  unsigned long timerMode3WaterPumpDuration; //duration to activate water pump in milliseconds
  unsigned long timerMode3LastCycletime;
  unsigned long timerMode3MaxML;
  int calibrationFactor; //defualt was float 4.5
  bool enabled = true;
  char sta_ssid[15];
  char sta_password[15];
  char system_password[15];
} _EEPROM;

long currentMillis = 0;
long previousMillis = 0;
int interval = 1000;
volatile byte pulseCount = 0;
byte pulse1Sec = 0;
float flowRate = 0.0;
unsigned long flowMilliLitres = 0;
unsigned int totalMilliLitres = 0;
float flowLitres = 0.0;
float totalLitres = 0;

void IRAM_ATTR pulseCounter() {
  pulseCount++;
}

bool ARelayIsOn = false;

struct Relay
{
  const byte RelayPin;
  bool readyToTurnOn;
  bool isOn;
  unsigned long StartTime;
}

Relays[4] =
{
  {26, true, false, 0},
  {25, true, false, 0},
  {33,  true, false, 0},
  {32, true, false, 0}
};

void TurnRelayOn(int r) {
  ARelayIsOn = true;
  Relays[r].readyToTurnOn = false;
  Relays[r].isOn = true;
  Relays[r].StartTime = millis();  // Start of OnInterval
  digitalWrite(Relays[r].RelayPin, LOW);  // LOW for ON
  _EEPROM.timerRuntimes[r]++;
}

void TurnRelayOff(int r) {
  ARelayIsOn = false;  // Only one is on at a time
  Relays[r].readyToTurnOn = false;
  Relays[r].isOn = false;
  Relays[r].StartTime = millis();  // Start of OffInterval
  digitalWrite(Relays[r].RelayPin, HIGH);  // HIGH for OFF
}

String masterIP = "";

void _writeEEPROM () {
  EEPROM.put(addr, _EEPROM);
  EEPROM.commit();    //Store data to EEPROM
  Serial.println("EEPROM WRITE");
}
void sendStruct() {
  Udp.beginPacket(_EEPROM.masterIP, ServerPort);
  Udp.print(nodeID0);
  Udp.write((uint8_t*)&_EEPROM, sizeof(_EEPROM)); //cast to bytes
  Udp.print(nodeID1);
  Udp.endPacket();
}


void default_params() {
  for (int i = 0; i >= 3; i++) {
    _EEPROM.timerTimes[0][i] = 30000;
    _EEPROM.timerTimes[1][i] = 30000;
    _EEPROM.timerTimes[2][i] = 30000;
    _EEPROM.timerTimes[3][i] = 30000;
    _EEPROM.timerRuntimes[i] = 0;
  }
  _EEPROM.frequency = 10;
  _EEPROM.sta_ssid[13] = '\0';
  _EEPROM.sta_password[13] = '\0';
  sprintf(_EEPROM.system_password, "%s", "password");
  _EEPROM.enabled = true;
  _EEPROM.timerMode = 3;
  _EEPROM.masterIP[19] = '\0';
  DateTime currentTime = DateTime(21, 4, 20, 4, 20, 20); //define date and time object
  rtc.adjust(currentTime); //configure the RTC with objectw
  _writeEEPROM ();
}

#include "handlePacket.h"
#include "timerModes.h"
#include "htmlFunctions.h"
#include "json.h"
void setup() {
  SPIFFS.begin();
  rtc.begin();
  Serial.begin(115200);
  Serial.println("001");
  EEPROM.begin(1024);  //Initialize EEPROM
  Serial.println("002");
  EEPROM.get(addr, _EEPROM);

  for (int i = 0; i <= 3; i++) {
    pinMode(relay[i], OUTPUT);
    digitalWrite(relay[i], HIGH);
    _EEPROM.timerRuntimes[i] = 0;
  }
  pinMode(fullFloatSensor, INPUT_PULLUP); // PULL UP float sensor
  pinMode(emptyFloatSensor, INPUT_PULLUP); // PULL UP float sensor
  //  if (epoch32.Epoch32Time() == 946684800) {//fix
  //    ESP.restart();
  //  }
  pinMode(flowSENSOR, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(flowSENSOR), pulseCounter, FALLING);
  WiFi.persistent(0);
  WiFi.mode(WIFI_AP_STA);
  WiFi.begin(_EEPROM.sta_ssid, _EEPROM.sta_password);
  sprintf(_EEPROM.system_password, "%s", "password");
  _EEPROM.sta_ssid[14] = '\0';
  _EEPROM.sta_password[14] = '\0';
  Serial.println("Initializing");
  Udp.begin(localUdpPort);
#include "serverHandles.h"
  ElegantOTA.begin(&update_server);    // Start ElegantOTA
  update_server.begin();
  server.begin();
  sprintf(_EEPROM.system_password, "%s", "password");
  _EEPROM.sta_ssid[14] = '\0';
  _EEPROM.sta_password[14] = '\0';
}

void handleTimers(bool a, bool b, bool c, bool d) {
  digitalWrite(relay[0], a);
  digitalWrite(relay[1], b);
  digitalWrite(relay[2], c);
  digitalWrite(relay[3], d);
}

void handleTimerStates(int a, int b, int c, int d) {
  timerStates[0] = a;
  timerStates[1] = b;
  timerStates[2] = c;
  timerStates[3] = d;
}

void handleGetTime() {
  DateTime currentTime = rtc.now();    //get the time from the RTC
  //declare a string as an array of chars
  sprintf(str, "%d/%d/%d %d:%d:%d",     //%d allows to print an integer to the string
          currentTime.year(),   //get year method
          currentTime.month(),  //get month method
          currentTime.day(),    //get day method
          currentTime.hour(),   //get hour method
          currentTime.minute(), //get minute method
          currentTime.second()  //get second method
         );
  Serial.println(str); //print the string to the serial port
}

void loop() {
  DateTime now = rtc.now();
  secondstime = now.secondstime();
  server.handleClient();//Checks for web server activity
  recPacket();
  processTimers();
  if (adminEnabled) {
    update_server.handleClient();
  }

  if (WiFi.status() == WL_CONNECTED) {
    apSwitchMillis = millis();
    if (millis() - lcdMillis >= 5000) {
      WiFi.softAPdisconnect (true);
      lcdMillis = millis();
    }
  } else {
    if (millis() - apSwitchMillis >= apTimeout) {
      if (!apState) {
        WiFi.softAP(ssid, password, 11, false, 8);
        WiFi.softAPConfig(local_IP, local_IP, subnet);
        apState = true;
      }
      apSwitchMillis = millis();
    }
  }

  if (millis() - now2 >= 5000) {
    handleGetTime();
    now2 = millis();
  }

  if (millis() - now4 >= 800) {
    for (int i = 0; i <= 3; i++) {
      _EEPROM.timerRunning[i] = digitalRead(relay[i]);
    }

        if (digitalRead(fullFloatSensor) == HIGH) { //if  low then is full //high level sensor
          fullFloatSensorStatus = false;
        } else {
          fullFloatSensorStatus = true;
        }
        if (digitalRead(emptyFloatSensor) == HIGH) { //if low then is NOT empty //low level sensor
          emptyFloatSensorStatus = true;
        } else {
          emptyFloatSensorStatus = false;
        }
    photoSensor = analogRead(photoSensorpin);
    if (photoSensor >= 55) {
      lightsOn = true;
    } else {
      lightsOn = false;
    }
    sendStruct();
    now4 = millis();
  }
}

i know my code could use alot of improvement. i am just trying to get things in working order and then i want to rewrite most of it to be cleaner and more efficient. please forgive my lack of structure

and

if (Mix && millis() - prevMixmillis >= 20000)

usually bad from to mix logic conditions with timer tests. Suggest you check the timer first and then ignore if the logic condition does not match.
See my tutorial on How to write Timers and Delays in Arduino for a simple timer class that lets you define and keep track of multiple timers

So you this isn't vaild?

if (Mix && millis() - prevMixmillis >= 20000)Blockquote

Is the above any different than,

if (Mix && ((millis() - prevMixmillis ) >= 20000)) {

Except that i think in the later if waits for conditional statement in parenthesees first

what is any different from all the above from than below,

if (Mix) {
if(millis() - prevMixmillis  >= 20000){

I was thinking more of

if(millis() - prevMixmillis  >= 20000){ // timer expired
if (Mix) { // do something

Now using just millis() does not tell you if the timer is 'running' so you may need to add a boolean to see if that timer should be checked.
That may be what you Mix is doing but I have not checked your code in enough detail to be certain.
The millisDelay class keeps an internal bool to keep track of whether the timer is running or not.

An ESP32, I'd just use freeRFTOS, the built in OS of the ESP32, put each timing section in its own task, and use vTaskDelayUntil. Most people will state you don't need to use freeRTOS but it is already running on the ESP32, why not use it?

If you want to go the vTask way, there is a simple example at the top of my
Multi-tasking in Arduino tutorial
(Blink_AnalogRead_FreeRTOS.ino)
Edit actually using Arduino_FreeRTOS.h, but should be similar to the ESP32 calls.
see FreeRTOS - ESP32 - — ESP-IDF Programming Guide latest documentation
for the ESP32 methods

I still have not figured out what happening. all i can tell is sometimes when i update _EEPROM.timerMode3LastCycletime with "secondstime" the value is way off. i could really use some assistance with this

I beginning to think i might be getting a bad reading from the RTC module. my wiring is good. I didnt use pull up resistors i wonder if my breakout board has them. i figure it already has them

Its not important that the timer start immediately after 7200 seconds. For some reason i cant wrap my head around a way to check seconds time to make sure theres no error. at this point i just need something to work other than averaging the values returned by rtc.

The error must happen fast because my rtc timestamp never reports any problem when the error happens. only because the time trys to start and overwrites the value in the eeprom with the bad value its a problem

If you break your code down in to separate tasks it might become clear to you what is actually happening and let you test each task on its own.

I think the code along with my explanation is clear enough. I simply asked if anyone seen any error in the code. I'm pretty sure there's no error in the code. I think it's rtc i2c malfunction

Write a small(er) program and convince yourself that the basic functions of the RTC are operating correctly.

Is there anything else on you i2c lines?

a7

Nothin else on the i2c line. I've added 5k pull ups and some decoupling caps now just waiting to see if it happens again

Not on the I2C data line, I hope?

No not on i2c

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