Tricky code to write

So I am trying to make a device on Pro Mini that takes high input from ESP32 ((yes noisy(maybe from power source) and only 3.3V)), activates an output for max 4 seconds. If input is still high after timeout, output stays low until input cycled low/high again. Once input goes high again, timer restarts. If input is turned low, before timer expires, output goes off and timer stops. I tried a couple AI generators and got close, but it still just immmediatley turns ouptput low/high on reaching end of timer. I wonder if anybody has any ideas. TIA D

const int INPUT_PIN = 2;   // Define input pin
const int OUTPUT_PIN = 3;  // Define output pin

const unsigned long TIMEOUT = 4000; // 4 seconds in milliseconds
const int STABLE_READS = 5; // Number of consecutive stable reads needed

bool stableInput = LOW;
bool outputOn = false;
bool needsCycle = false;
unsigned long timer = 0;

int highCount = 0;
int lowCount = 0;

void setup() {
  Serial.begin(9600);
  pinMode(INPUT_PIN, INPUT);
  pinMode(OUTPUT_PIN, OUTPUT);
  digitalWrite(OUTPUT_PIN, LOW);
  
  stableInput = digitalRead(INPUT_PIN);
  Serial.println("=== STARTED ===");
}

void loop() {
  bool rawInput = digitalRead(INPUT_PIN);
  unsigned long now = millis();
  
  // Count consecutive readings
  if (rawInput == HIGH) {
    highCount++;
    lowCount = 0;
  } else {
    lowCount++;
    highCount = 0;
  }
  
  // Check for stable HIGH
  if (highCount >= STABLE_READS && stableInput == LOW) {
    stableInput = HIGH;
    Serial.println("\n>>> Input went HIGH");
    
    if (!needsCycle) {
      outputOn = true;
      timer = now;
      digitalWrite(OUTPUT_PIN, HIGH);
      Serial.print("OUTPUT ON - Timer started at: ");
      Serial.println(timer);
    } else {
      Serial.println("BLOCKED - Needs cycle to LOW first");
    }
  }
  
  // Check for stable LOW
  if (lowCount >= STABLE_READS && stableInput == HIGH) {
    stableInput = LOW;
    unsigned long elapsed = now - timer;
    
    Serial.println("\n<<< Input went LOW");
    Serial.print("Elapsed time was: ");
    Serial.print(elapsed);
    Serial.println("ms");
    
    outputOn = false;
    needsCycle = false;
    digitalWrite(OUTPUT_PIN, LOW);
    Serial.println("OUTPUT OFF - Everything reset");
  }
  
  // Check timeout while output is on
  if (outputOn) {
    unsigned long elapsed = now - timer;
    
    if (elapsed >= TIMEOUT) {
      Serial.println("\n!!! TIMEOUT REACHED !!!");
      Serial.print("Elapsed: ");
      Serial.print(elapsed);
      Serial.println("ms");
      
      outputOn = false;
      needsCycle = true;
      digitalWrite(OUTPUT_PIN, LOW);
      Serial.println("OUTPUT OFF - Need cycle to re-enable");
    }
  }
  
  delay(1); // Small delay between reads for debouncing
}

This output from Serial Port is from BOOT, it sensed input LOW, I put input HIGH, wait for timer, immediatley goes high again.

=== STARTED ===

<<< Input went LOW
Elapsed time was: 4ms
OUTPUT OFF - Everything reset

>>> Input went HIGH
OUTPUT ON - Timer started at: 7556

!!! TIMEOUT REACHED !!!
Elapsed: 4000ms
OUTPUT OFF - Need cycle to re-enable

<<< Input went LOW
Elapsed time was: 4028ms
OUTPUT OFF - Everything reset

>>> Input went HIGH
OUTPUT ON - Timer started at: 11659

!!! TIMEOUT REACHED !!!
Elapsed: 4000ms
OUTPUT OFF - Need cycle to re-enable

<<< Input went LOW
Elapsed time was: 4021ms
OUTPUT OFF - Everything reset

Check out the tutorials regarding State Machines. That will yield a more manageable design that will be much easier to code.

1 Like

I'm not surprised the AI attempts failed. The description has to be clearer. I got lost between the sources of the various inputs and outputs you were referring to.

Anyway, it appears you have two use cases:

  1. The ESP32 delivers a pulse lasting less than or equal to 4 seconds to the Pro Mini so X (to be defined) should happen on the Pro Mini.
  2. The ESP32 delivers a pulse lasting more than 4 seconds to the Pro Mini, then that pulse should be ignored and the Pro Mini should wait until the next pulse and react on that.

Is that correct and complete ?

It could help if you gave more background to the problem you are trying to solve to see it in context.

Regardless of what you are trying to do, there some code style ‘errors’. I doubt they are causing the problem you are having, but it’s at least unconventional.

bool variables should only be given the values true or false.

digitalRead returns an int, so make rawInput and stableInput of type int.

There may be others. I didn’t check all the code, just a quick scan.

ESP outputs from a pin, to input pin on Pro. The ESP is running web server. I click button on webpage to put output high. If page does not respond well, I may not be able to switch the Pro’s input low for some time. I want it to “cutout” the output after 4 seconds, and not allow Pro output pin to go high unless input has gone low again. But I want to be able to interrupt the countdown any time I get an “OFF” message through, then start timer over when I do another high input. I suppose there is a way to run two switches and a servo all from the ESP but that would be far beyond the ability of a self declared Copy/Paste coder. I have actually started to get some of it though. Have seen how the IDE helps you format if you type it properly. I checked the State/Commutate tutorial. I think I make sense of it and might even be able to spin some into another attempt I tried.

Tee Hee You get what you pay for with free AI

No, it returns HIGH or LOW.

You can properly handle this easily, and get your boolean true/false, washing your hands of the poor choice around digitalRead() and digitalWrite(), viz:

    bool rawInput = digitalRead(INPUT_PIN) == HIGH;

Or maybe it should be true if LOW. This is also handled easily and you get code that reads better:

// up top
const auto PRESSED = LOW;


// then anywhere else

bool rawInput = digitalRead(INPUT_PIN) == PRESSED;

a7

FYI I was able to get a few different AI attempts to work without all the stable read requirements by using a pulldown on the Pro input pin. Just the loops always immediately turned the output back on at expiration of timer.

The ESP32 uses 3.3V logic. If the Pro Mini uses 5V logic, a logic level converter is normally used to handle the transition properly.

Yes, the hardware mismatch is not a good choice. It may all get re-upped instead of scraping the goodie drawer to attempt a gadget. But there seems to be no issue getting the end action to occur. It just is not coded properly to get the scenarios I want.

Taking a read of this: Yet another Finite State Machine introduction.._gaMjMxMjY5NzYwLjE3NjIzNjA1MjA._ga_NEXN8H46L5czE3NjIzNjUzODgkbzIkZzAkdDE3NjIzNjUzODgkajYwJGwwJGg1MTM1NDA2NA..

Chrono….hmmmmm

This is an example of one that just “clicks”. Turns back on right away, timer never stops running. It works fine with hardware even though noisy. (ie: Output goes ON/OFF perfect with cycle of input HIGH/LOW)



// Define input and output pins
int inputPin = 2;
int outputPin = 3;

// Define variables for timer and interrupt
unsigned long timer = 0;
boolean interrupt = false;

void setup() {
  // Set input and output pins
  pinMode(inputPin, INPUT);
  pinMode(outputPin, OUTPUT);
}

void loop() {
  // Check if input pin is high
  if (digitalRead(inputPin) == HIGH) {
    // Check if timer has reached 4 seconds
    if (millis() - timer >= 4000) {
      // Set output pin to low
      digitalWrite(outputPin, LOW);
    }
  }
  // Check if input pin is low
  else {
    // Set output pin to high
    digitalWrite(outputPin, HIGH);
    // Reset timer
    timer = millis();
  }
  // Check if interrupt has been triggered
  if (interrupt) {
    // Reset timer
    timer = millis();
    // Reset interrupt
    interrupt = false;
  }
}

// Interrupt function
void interruptFunction() {
  // Set interrupt to true
  interrupt = true;
}

// Reference: https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

From post #5:

ESP outputs from a pin, to input pin on Pro. The ESP is running web server. I click button on webpage to put output high.

1) So the normal use case is that ESP sets a pin high. This pin is read by the Pro Mini which sets its designated output pin high.

If page does not respond well, I may not be able to switch the Pro’s input low for some time. I want it to “cutout” the output after 4 seconds, and not allow Pro output pin to go high unless input has gone low again.

2) Timeout use case: If the Pro Mini's designated output pin has been HIGH for more than 4 seconds, it should then go low and wait until the (pro Mini's) input has gone low before further responding

But I want to be able to interrupt the countdown any time I get an “OFF” message through, then start timer over when I do another high input.

3) Can you be clearer ? What "OFF" message ? What countdown ? Is this the 4 second timeout counter ?

This seems to be one of the problems with using AI to get a solution. Instead of it challenging an unclear or ambiguous statement, and coming back to the user to get a clearer requirements statement, it simply does a best guess, generates just something, and waits for the user to try to explain where it goes wrong.

Yes, the 4 second timer. I want it to stop counting when I am able to make a timely LOW input (before timer expiration). Then start at zero when I make a HIGH input again. Every iteration has either timer runs all the time or not at all.

I did have one point where it would respond properly to the timer, go off and stay off, but the timer still ran. So it could happen where I want to turn it on, and the timer has .25 seconds left and shut right off soon.

Both are correct! The variable type returned by the digitalWrite() function is an int.

https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/Arduino.h#L136

However, the function itself will return the pre-defined integer values of either HIGH or LOW depending on the state of the pin. The values HIGH and LOW are defined as:

#define HIGH 0x1
#define LOW 0x0

[https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/Arduino.h#L40

This happens to conveniently map onto a boolean type since False = 0 and True > 0, so the code still works. However I do believe that following strict variable typing rules:

// int stableInput = LOW;

would be correct*. TBH, although I prefer strict matching of variable types, I am not sure what the "proper" accepted approach is here? Arguably, although very unlikely, if ever a different value was assigned to LOW, int would still work, whereas bool would not. I guess it depends on how strict you want to be?

  • see further discussion below!

Yeth. It's simple enough to play by the rules.

The rules you glean from the documentation for digitalWrite():

Reads the value from a specified digital pin, either

HIGH

or

LOW

Not the ones you peek under the hood to look at. The hole point of having details you don't need or maybe even get to know is lost if you assume the implementation is invariant.

I will say I don't expect the ground to shift underneath us on this particular matter, so just call it conservative to be "strict".

a7

1 Like
case 1: 


           2 seconds                                           1 second
          _________                                            _______
ESP   ____|        |___________________________________________|      |______________________________



           2 seconds                                           1 second
PRO       _________                                            _______
MINI  ____|        |___________________________________________|      |______________________________



------------------------------------------------------------------------------------------------------

case 2:


           5 seconds                                           1 second
          ______________________                                _______
ESP   ____|                     |_______________________________|      |______________________________



           4 seconds                                           1 second
PRO       __________________                                    _______
MINI  ____|                 |___________________________________|      |______________________________

------------------------------------------------------------------------------------------------------

I'm trying to get a time line out of your statements. Maybe that could make clearer how the Pro Mini is supposed to respond to the the output of the ESP32.

In case 1 above, the ESP32 sends a 2 second pulse to the Pro Micro. Then after a pause, a 1 second pulse. The Pro Micro responds by reflecting to the 2 second pulse on its output, then again, the one second pulse.

In case 2 above, the ESP32 delivers a 5 second pulse. The Pro Mini reads that pulse and outputs it but, because it is too long, it terminates after a 4 seconds timeout. When the ESP32 after a pause sends a 1 second pulse, then that is again reflected on
the output of the Pro Mini.

Is that correct and is there anything else to consider ?

Here's an example of a state machine that should do what you need (untested)

// Define input and output pins
const int inputPin = 2;
const int outputPin = 3;

int inputState;

unsigned long startTime;
const unsigned long timeout = 4000;

// all the states
enum { IDLE, OUTPUT_ON, OUTPUT_OFF, WAIT_FOR_LOW };
int state;

void setup() {
  Serial.begin(115200);
  // Set input and output pins
  pinMode(inputPin, INPUT);
  pinMode(outputPin, OUTPUT);
  digitalWrite(outputPin, LOW);
  beginState( WAIT_FOR_LOW );
}

void loop() {
  inputState = digitalRead(inputPin);

  updateState();
}

void beginState( int newState) {
  switch ( newState) {
    case IDLE:
      Serial.println("Idle. Waiting for input to go HIGH");
      break;

    case OUTPUT_ON:
      Serial.println("Output ON");
      digitalWrite(outputPin, HIGH);
      startTime = millis();
      break;

    case OUTPUT_OFF:
      Serial.println("Output OFF");
      digitalWrite(outputPin, LOW);
      break;

    case WAIT_FOR_LOW:
      Serial.println("Waiting for input to go LOW");
      break;
  }
  state = newState;
}

void updateState() {
  switch ( state ) {
    case IDLE:
      // check input pin and transition as needed
      if ( inputState == HIGH ) {
        beginState( OUTPUT_ON);
      }
      break;

    case OUTPUT_ON:
      // check to see if input is LOW or time has expired
      if ( inputState == LOW || millis() - startTime >= timeout ) {
        beginState( OUTPUT_OFF );
      }
      break;

    case OUTPUT_OFF:
      // output has been turned off so just transition
      beginState( WAIT_FOR_LOW );
      break;

    case WAIT_FOR_LOW:
      // wait until input is LOW 
      if ( inputState == LOW ) {
        beginState( IDLE );
      }
      break;
  }
}


That looks right

Thank you for doing that work. I will be trying that shortly and will let you know. See below for more.