Using Analog Input to Both turn on and turn off a Digital Output

Hello Forum Members, I need some help on a project. I am attempting to make a button array to trigger multiple digital outputs using one Analog Input, I've got it to work but I've run into some issues trying to get the same button to both power on and off the Digital Output. I have tried multiple solutions such as counters or delays and none seem to work for my application or work in general. Essentially my goal would be to have the same Analog Input value be able to power on a channel and power off a channel, I have tried using a counter as well as threshold values and neither seem to work for my application as the goal is to be able to trigger 10 digital outputs using the singular input channel. Any help would be greatly appreciated, Thank you in advance.

void setup() {
  Serial.begin(9600);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  
  pinMode(A0, INPUT);

}

void loop() {

  int sensorValue = analogRead(A0);
  Serial.println(sensorValue);

  if((analogRead(A0) > 160) && (analogRead(A0) <185) && digitalRead(2) == LOW) {
      digitalWrite(2, HIGH);
  }

  if((analogRead(A0) > 160) && (analogRead(A0) <185) && digitalRead(2) == HIGH) {
    digitalWrite(2, LOW);
  }

For easy help, post your project schematic wiring.

You are using the 'state' of the button to and not the 'edge' ( the transition ).
This means that as long as the analogread returns a value between 160 and 185 you are toggling output 2 ( that not what you want to do ).
The easier way is using an analog button library ( never used, search for others too ), you are looking for the 'click event' I think

I agree, you need to track the state of the button-equivalent and react to the edges you observe.

If you got this to work

    bool buttonReading = digitalRead(buttonPin) == LOW;

that is using a regular old digitally read pushbutton. Follow the pattern exposed in the IDE example 02. Digital / State Chnage Detection

and this competent article that goes with

https://docs.arduino.cc/built-in-examples/digital/StateChangeDetection/

then all you need to do here is

    int analogValue = analogRead(A0);
    bool buttonReading = analogValue > 160 && analogValue <185;

But I suspicion you are not saying where this is heading, and indeed where it is heading may change how you want to spend time right now getting one button to work like a button.

So, where is this headed? :expressionless:

a7

Here's an example of noticing and acting on changes in an analog value rather than on the level of the analog value:

// Analog StateChange Detection for 
// https://wokwi.com/projects/391124361363049473
// for https://forum.arduino.cc/t/esp8266-analog-input-triggering-issue-for-one-shot/1229757/24
// and https://forum.arduino.cc/t/using-analog-input-to-both-turn-on-and-turn-off-a-digital-output/1292092/5


int lastButtonState = 0;

const int AnalogPin = 15;

int currentButtonState = 0;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;

void setup() {
  Serial.begin(115200);
}

void loop() {
  currentButtonState = analogRead(AnalogPin) / (1023/25); // make into distinct ranges
  if (currentButtonState != lastButtonState && millis() - lastDebounceTime >= debounceDelay) {
    lastDebounceTime = millis();
    Serial.print(char('A'+currentButtonState));
    lastButtonState = currentButtonState;
  }
}

The trick to state change detection is remembering what the value was before, so you can compare the current value to the past value and see if there was a change.

The second trick I think you are looking for is that after you notice a change, you want to toggle something. One way is to check the state and switch it:

if(digitalRead(2) == LOW) {
   digitalWrite(2, HIGH);
} else {
   digitalWrite(2, LOW);
}

As in:

// Analog StateChange Detection for
// https://wokwi.com/projects/391124361363049473
// for https://forum.arduino.cc/t/esp8266-analog-input-triggering-issue-for-one-shot/1229757/24

int lastButtonState = 0; // integer to remeber a range of distinct states

const int AnalogPin = 15;

int currentButtonState = 0;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
char baseChar = 'A';
void setup() {
  Serial.begin(115200);
  pinMode(13,OUTPUT);
}

void loop() {
  currentButtonState = analogRead(AnalogPin) / (1023 / 25); // transform into distinct ranges
  if (currentButtonState != lastButtonState && millis() - lastDebounceTime >= debounceDelay) {
    lastDebounceTime = millis();
    Serial.print(char(baseChar + currentButtonState));
    lastButtonState = currentButtonState;

    if (currentButtonState == 3) {
      if (digitalRead(13) == LOW) {
        digitalWrite(13, HIGH);
        baseChar = 'a';
      } else {
        digitalWrite(13, LOW);
        baseChar = 'A';
      }
    }
  }
}

Note that at each level of the (transformed) A0 values, the character is only printed once, because of state-change detection on the variable. And the character set shifts between uppercase and lower case when the range for "D" is entered.

This sounds similar to what @icefreezer7 was working on the other day with a ten-position switch potentiometer.

It made me think of a game (since their application was a flight sim iirc) so I cooked up this sketch for fun. Is this similar to what you're hoping to do?

/************* MARS Industries Welcome Series Presents: ********************
         **********   Cobra Rattler Armaments Package    ************
          **********        (tenModeSelectorPotgame)           ************
 ***************************************************************************

   Rough Calibration based on preliminary sketch supplied by icefreezer7
   in this thread of the Arduino Forum
   https://forum.arduino.cc/t/rotary-switch-pot-only-outputting-0/1290810/55
    Position 1: 0 to 0
    Position 2: 112 to 114
    Position 3: 225 to 227
    Position 4: 340 to 342
    Position 5: 454 to 455
    Position 6: 569 to 570
    Position 7: 683 to 685
    Position 8: 797 to 799
    Position 9: 911 to 913
    Position 10: 1023 to 1023

    circuit: potentiometer A2, GND and +5V
             LED to pin 9
             Use Serial Monitor at 115200 baud

  by Hallowed31
   2024-08-13

  Free software as in gratis. No guarantees. Use at your own risk.
  Redistribute if you want to. Party on.

*/
int weaponsVal;
int lastWeaponsVal;
int weaponsPin = A2;
byte weaponMode; // to drive the state machine
byte lastWeaponMode; // to wrangle certain behavior like endlessly Serial printing
boolean newInfo;
int statusLed = 9;
unsigned long statusTimer = 0;
unsigned long statusTime = 0;
int timeToReadMessageDelay = 3500; // default 3500
unsigned long previousMillis = 0;
const long interval = 1000; // for countdown timer, default 1000
int timer = 30; // also for countdown timer

void setup() {
  pinMode(weaponsPin, INPUT);
  pinMode(statusLed, OUTPUT);
  digitalWrite(statusLed, LOW);
  Serial.begin(115200);
  shortLineSpacing();
  Serial.println(F("      MARS Industries Welcome Series Presents:\n"));
  Serial.println(F("      Cobra Rattler Armaments Package\n"));
  delay(timeToReadMessageDelay);
  longLineSpacing();
  weaponsVal = 0;
  lastWeaponsVal = -1;
  weaponMode = 0;
  lastWeaponMode = -1;
  newInfo = false;
}

void loop() {
  weaponsVal = analogRead(weaponsPin);
  if (weaponsVal != lastWeaponsVal) {
    // Serial.println(weaponsVal);
    newInfo = true;
    switch (weaponsVal) {
      case 0 ... 90:
        weaponMode = 0; // disarmed
        statusTimer = 0;
        break;
      case 100 ... 190:
        weaponMode = 1; // chaff
        statusTimer = 100;
        break;
      case 200 ... 290:
        weaponMode = 2;  // GAU-8
        statusTimer = 200;
        break;
      case 300 ... 390:
        weaponMode = 3; // lasers
        statusTimer = 300;
        break;
      case 400 ... 490:
        weaponMode = 4; // flares
        statusTimer = 400;
        break;
      case 500 ... 600:
        weaponMode = 5; // sidewinder
        statusTimer = 500;
        break;
      case 650 ... 740:
        weaponMode = 6; // hellfire
        statusTimer = 600;
        break;
      case 750 ... 825:
        weaponMode = 7; // willy pete
        statusTimer = 700;
        break;
      case 875 ... 975:
        weaponMode = 8; // propaganda
        statusTimer = 800;
        break;
      case 1000 ... 1023:
        weaponMode = 9; // <classified>
        statusTimer = 1000;
        break;
    }
    lastWeaponsVal = weaponsVal;
  }
  informStatusAndArmWeapons();
  blinkStatus();
}

void informStatusAndArmWeapons() {
  if (newInfo == true) {
    if (weaponMode != lastWeaponMode) {
      switch (weaponMode) {
        // newInfo = false;
        case 0:
          Serial.println(F("      Weapons Disarmed\n"));
          wStage0();
          break;
        case 1:
          Serial.println(F("      Chaff Deployed\n"));
          wStage1();
          break;
        case 2:
          Serial.println(F("  GAU-8/A Avenger Armed: BRRRRRRRRAAAP!\n"));
          wStage2();
          break;
        case 3:
          Serial.println(F("      Lasers Armed\n"));
          wStage3();
          break;
        case 4:
          Serial.println(F("      Flares Armed\n"));
          wStage4();
          break;
        case 5:
          Serial.println(F("      Sidewinder Missiles Armed\n"));
          wStage5();
          break;
        case 6:
          Serial.println(F("      Hellfire Rockets Armed\n"));
          wStage6();
          break;
        case 7:
          Serial.println(F("      Willy Pete Armed\n"));
          wStage7();
          break;
        case 8:
          Serial.println(F("    Propaganda Flyers Dropped\n"));
          wStage8();
          break;
        case 9:
          Serial.println(F("   <CLASSIFIED> Package Deployed\n"));
          wStage9();
          timer = 30; // restart countdown timer every time
          break;
      }
      lastWeaponMode = weaponMode;
      newInfo = false;
    }
  }
}

void wStage0() {
  // all weapons idle
}
void wStage1() {
  // maybe code for stopping chaff firing sequence
}
void wStage2() {
  // maybe button code to light 'em up with the ma'CHEEN gun
}
void wStage3() {
  // maybe button code to light 'em up with the lasers
}
void wStage4() {
  // maybe button code to deploy flares
}
void wStage5() {
  // maybe button code to deploy missiles
}
void wStage6() {
  // maybe button code to deploy rockets
}
void wStage7() {
  // maybe button code to deploy white phosphorus
}
void wStage8() {
  Serial.println(F("Brainwashing Material Distributed courtesy of Cold Slither\n"));
  delay(timeToReadMessageDelay);
}
void wStage9() {
  // maybe decending tone BEEEEEEUUUUUuuuu
}

void blinkStatus() {
  if (millis() - statusTime >= statusTimer) {
    statusTime = millis();
    digitalWrite(statusLed, !digitalRead(statusLed));
  }
  if (weaponMode == 9) {
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
      previousMillis = currentMillis;
      Serial.print(F("You now have T- "));
      Serial.print(timer);
      Serial.println(F(" seconds to reach minimum safe distance\n"));
      timer--;
      if (timer == -1) {
        Serial.println(F("<CLASSIFIED> device detonated\n"));
        timer = 30;
      }
    }
  }
}

void shortLineSpacing() {
  for (int i = 0; i < 15; i++) {
    Serial.println();
  }
}
void longLineSpacing() {
  for (int i = 0; i < 25; i++) {
    Serial.println();
  }
}
1 Like

This sounds like a good use for a shift register. My recommendation would be an output register like the SN74HC595. Bonus: This will save on the number of pins your using as well.

Without more context on "what" you are triggering, i'm not going to be much help.

1 Like

What you logically need is something like an analog version of the bounce library.

The problem with code like you had was suppose you have a value 170 and assume the pin 2 logically low>

First time through loop: the code will see that Analog value is within your range and pin 2 is low, so it sets it high.

It follows through to your next if... Yes it is within range and value is high so not it sets it low again... Next time through loop it will repeat...

Suppose you had a function, like:

uint8_t get_current_analog_band() {
  uint16_t analog_value = analogRead(A0);
  if ((analog_value  > 160) && (analog_value  <185)) return 1;
  // ... other ranges as well.
  return 0;
}

Now suppose your loop code was something like:

uint8_t last_band = 0xff; // something not valid to start.

void loop() {
  uint8_t new_band =  get_current_analog_band();
  if (new_band != last_band) {
    // maybe want to make sure it is stable.
    for(;;) {
      delay(1); 
      uint8_t check_band =  get_current_analog_band();
      if (check_band == new_band) break;
      new_band = check_band;
    }
    // now process the bands.
    if (new_band == 0) {
        // some boards may not like this... Others like teensy might have digitalToggle(2);
        if (digitalRead(2)) digitalWrite(2, LOW);
        else digitalWrite(2, HIGH);
    last_band = new_band;
  }
}

Note all typed-on fly so probably issues.
Also unclear exactly what you want to do, if for example you go from band 1 to band 2.
you may for example want to turn off logical 1 and logically turn on 2...
Which would be easy to do as you have what the previous band was.

Hope that helps

How many buttons?

Hello Everybody, thank you all for the help, I haven't had the time to go through all the responses in depth to test and learn but I'm slowly making my way through, I saw a few people ask for more context so here we go, for starters and I'm sure this is obvious I have very limited programming experience, what I am attempting to do is build a custom smart Power Distribution board, using a 10 button array to individually control 10 outputs on the Arduino, the output channels will be used to trigger a relay turning the 12V channels on the PDU on and off, the code I posted was the closest I got to a consistent working prototype. Essentially what my setup is a bunch of resistors staggered in between multiple buttons essentially adding resistance to the analog input and changing the value on a single analog input pin allowing me to only use one input for multiple buttons, I'm using those values to assign the button to a channel. the main issue I've been having is once digital output 2 is triggered high it does not want to turn off, it appears to me the two sections of code begin to fight each other and I'm not sure how to correct this. I apologize in advance if this explanation doesn't make much sense as I am extremely tired, I just wanted to thank you all for your suggestions and provide further context as to what I'm doing.