Single input is triggering multiple pins randomly

Hi, I hope I'm posting this in the right place but I can't really put my finger on where this issue is happening. I'm still a beginner with this stuff so apologies if I've made any mistakes. Would appreciate suggestions

I'm trying to build a capacitive touch based directional padlock (i.e. N S E W based combination). Youtube tutorial link: No more Directional Locks.... could this be the end for Escape Rooms?! - YouTube. Code at bottom of post.

However! I was struggling to not get the cap touch modules not to constantly trigger when simply touching wood, metal, whatever I glued them to, so instead I devised a workaround using an ESP32 with the cap touch function to detect when some copper contacts connected to some touch pins had been touched and send a 100ms HIGH pulse to my arduino "NSEW pins".

The circuit looks like this

Code here:

int nPin = 17;
int sPin = 2;
int ePin = 0;
int wPin = 5;

int nBef = 0;
int sBef = 0;
int eBef = 0;
int wBef = 0;

int nAft;
int sAft;
int eAft;
int wAft;

int tol = 30;

void setup()
{
  Serial.begin(115200);
  delay(5000); // give me time to bring up serial monitor
  Serial.println ("MUTINY ESP32 NSEW TOUCH PIN CODE");
  Serial.print ("PIN");
  Serial.print ("\t");
  Serial.print ("BefVal");
  Serial.print ("\t");
  Serial.println ("AftVal");

  pinMode (nPin, OUTPUT);
  pinMode (sPin, OUTPUT);
  pinMode (ePin, OUTPUT);
  pinMode (wPin, OUTPUT);

  digitalWrite (nPin, LOW);
  digitalWrite (sPin, LOW);
  digitalWrite (ePin, LOW);
  digitalWrite (wPin, LOW);
  
}

void loop()
{

  nAft = touchRead(T0); // GPOI 4
  sAft = touchRead(T3); // GPOI 15
  eAft = touchRead(T8); // GPOI 33
  wAft = touchRead(T9); // GPOI 32

  Serial.print ("nTouch");
  Serial.print ("\t");
  Serial.print (nBef);
  Serial.print ("\t");
  Serial.println (nAft);

  Serial.print ("sTouch");
  Serial.print ("\t");
  Serial.print (sBef);
  Serial.print ("\t");
  Serial.println (sAft);

  Serial.print ("eTouch");
  Serial.print ("\t");
  Serial.print (eBef);
  Serial.print ("\t");
  Serial.println (eAft);
  
  Serial.print ("wTouch");
  Serial.print ("\t");
  Serial.print (wBef);
  Serial.print ("\t");
  Serial.println (wAft);

  Serial.println ();

if ((nBef > tol) && (nAft < tol)) {

  Serial.println ("North (green) trigger");
  digitalWrite (nPin, HIGH);
  delay (100);
  digitalWrite (nPin, LOW);
  Serial.println ();
  
}

if ((sBef > tol) && (sAft < tol)) {

  Serial.println ("South (blue) trigger");
  digitalWrite (sPin, HIGH);
  delay (100);
  digitalWrite (sPin, LOW);
  Serial.println ();
  
}

if ((eBef > tol) && (eAft < tol)) {

  Serial.println ("East (yellow) trigger");
  digitalWrite (ePin, HIGH);
  delay (100);
  digitalWrite (ePin, LOW);
  Serial.println ();

}

if ((wBef > tol) && (wAft < tol)) {

  Serial.println ("West (white) trigger");
  digitalWrite (wPin, HIGH);
  delay (100);
  digitalWrite (wPin, LOW);
  Serial.println ();
  delay(1000);

}

  nBef = nAft;
  sBef = sAft;
  eBef = eAft;
  wBef = wAft;

  delay(100);
  
}

The esp reads the 4 touch pins, every loop, and if it detects that the new touch read is low and the previous read is high (copper contact is being touched), it sends a 200ms high pulse to the Arduino pins A0-A3

I tested this with 4 LEDs and the 100ms high pulse is being sent, it lit the correct disparate LEDs in a way I would expect. It seems THIS code is working.

However, when the output pins are hooked up to the arduino running the code attached at the bottom of this post, there's random behaviour. When a copper contact is touched, the serial monitor on my Arduino tells me it is detecting voltage at three random pins consecutively. Really weird, this was shown after pressing the Up contact.

image

What I'm expecting the monitor to dispay is simply "Step:0 Input:U - Correct!" but this is telling me when the pulse is detected it also detects a bunch of other pins being triggered

I had expected the 200ms pulse , along with the INPUT_PULLUP function to be read by a single pin on the Arduino and trigger the mp3 soundbites etc.

The only thing I could think of is that even though I've programmed my esp to only send a voltage to a single pin on contact touch, there may be some interference or small voltage being applied to other pins. This voltage might be very small, hence, it didn't trigger my 4x LED troubleshooting setup.

Another error could be that the 200ms pulse and the cap touch modules the video do not behave in a way that is comparable and my "workaround" using the ESP32 is not going to make the code work as intended.

Current troubleshooting things ive tried, will update with any suggestions people make:

  • Increasing the delay on trigger to 200ms, no change
  • Slowing down the code by adding a 100ms delay once the arduino detected an input, no change.
  • Swapping out for a new arduino (but not a new ESP)

Code on arduino:

#include <AltSoftSerial.h>
#include <Wire.h>
#include <MD_YX5300.h>

/**
 * Electronic Direction Lock
 * 
 * Players must enter the correct sequence of directional input (i.e. U/D/L/R or N/E/S/W)
 * to activate a relay, releasing a maglock or some other output.
 * 
 * The script demonstrates various customisable behaviour:
 * - The correct input sequence may be of any length
 * - Each direction entered may be accompanied by audio or visual feedback to confirm input was registered
 * - Directions may be entered using pushbuttons, touch sensors, or any other momentary inputs arranged in 4-way cardinal directions
 * - Relay may either unlock automatically as soon as the last direction in the sequence is entered,
 *   or players may have to explicity press a "submit" button to submit their input
 * - Sequence of directions input may optionally be displayed on an LCD screen
 */

// DEFINES

// If AUTOCHECK is defined, the sequence entered by the player will be automatically checked 
// against the correct solution after every direction input, and will trigger the relay as soon
// as the last direction is correctly input.
// Otherwise, players must manually press a "submit" button to check the combination entered
#define AUTOCHECK

// If USE_AUDIO is defined, a command is sent via software serial interface to play a sound effect 
// on a connected YX5300 serial MP3 player board when an input is received, and when the correct sequence
// is input. If SFX are not required and no audio player is connected, we exclude the audio code to 
// prevent the script waiting for an acknowledgement from the sound player that will never be sent
#define USE_AUDIO

// INCLUDES
// For debouncing button input. See https://github.com/thomasfredericks/Bounce2

#include <Bounce2.h>

// CONSTANTS
// Enumerate each of the possible directional inputs
enum Dir {Down, Right, Up, Left};
// Also create descriptive names (for debugging/visual display)
const char* dirStr[] = {"D", "R", "U", "L"};
// Specify the Arduino GPIO pins to which those directional buttons are attached
const byte dirPins[4] = {A0,A1,A2,A3};
// This pin is driven low when correct sequence entered
const byte relayPin = 2;
// The number of directional steps in the solution
const int numSteps = 8;
// The correct sequence of directions that players must enter
const byte correctSequence[numSteps] = {Up, Up, Down, Down, Left, Right, Left, Right};

// GLOBALS
// Create an array of Bounce objects for each input button
Bounce dirSwitches[4] = {Bounce(), Bounce(), Bounce(), Bounce()};
// What step of the sequence is the player currently on?
int currentStep = 0;
#ifdef USE_AUDIO
  // Initialise a software serial interface on the approriate Rx/Tx pins (8/9)
  AltSoftSerial altSerial;
  // And create an MP3 object based on the serial connection
  MD_YX5300 mp3(altSerial);
#endif

// Is the sequence of inputs the player entered correct so far?
bool inputCorrect = true;

void setup() {
  // Initialise a serial connection (used for debugging only)
  Serial.begin(115200);
  Serial.println(__FILE__ __DATE__);

  // Attach a debouncer to every direction switch
  for(int i=0; i<4; i++){
    dirSwitches[i].attach(dirPins[i], INPUT_PULLUP);}
    
  // Initialise the output pin
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, HIGH);

  #ifdef USE_AUDIO
    // Initialise the serial interface to the MP3 player
    altSerial.begin(9600);
    mp3.begin();
    // Volume is a value from 0-30
    mp3.volume(30);
  #endif


}

void loop() {

  // Loop through all the inputs
  for(int i=0; i<4; i++){    
    // Update the state of this switch
    dirSwitches[i].update();
    // ...has it just been pressed?
    if(dirSwitches[i].rose()){

      #ifdef USE_AUDIO
        // Play the first sound effect on the SD card (i.e. "001.mp3")
        mp3.playTrack(1);
      #endif

      // Print some debug information
      Serial.print(F("Step:"));
      Serial.print(currentStep);
      Serial.print(F(" Input:")); 
      Serial.print(dirStr[i]);

      // If this input was the correct next value in the sequence
      if(i == correctSequence[currentStep]){
        // Debug only
        Serial.println(F(" - Correct!"));
      }
      // If the direction entered was incorrect
      else {
        // Set flag that user has made at least one incorrect entry in the sequence
        inputCorrect = false;
        // Debug only
        Serial.print(F(" - Incorrect!"));
        if(currentStep < numSteps){
          Serial.print(F(" Should have been "));
          Serial.println(dirStr[correctSequence[currentStep]]);
        }
        else {
          Serial.println(F(" Sequence too long."));
        }
      }
      // Continue to the next element of the sequence
      currentStep++;

      // If autocheck is enabled, we check the sequence entered and unlock 
      // as soon as the final input is entered
      #ifdef AUTOCHECK
        if(currentStep == numSteps){
          if(inputCorrect){
            onSolve();
          }
          else {
            onFailure();
          }
        }
      #endif
    }
  }

}

// This function is called when players submit the correct sequence of directions
void onSolve() {
  // Debug
  Serial.println("Solved!");

  #ifdef USE_AUDIO
    // Play the second sound effect on the SD card (i.e. "002.mp3")
    mp3.playTrack(2);
  #endif

  // Release the maglock
  digitalWrite(relayPin, LOW);
  
  delay(5000);
  digitalWrite(relayPin, HIGH);

  // And reset the controller
  currentStep = 0;
  inputCorrect = true;
}


// This function is called when player's submit an incorrect sequence of directions
void onFailure() {
  // Debug
  Serial.println("Incorrect!");

  #ifdef USE_AUDIO
    // Play the third sound effect on the SD card (i.e. "003.mp3")
    mp3.playTrack(3);
  #endif

  delay(2000);

  // And reset the controller
  currentStep = 0;
  inputCorrect = true;
}

It looks like you are making the Arduino supply power to six devices. Let the Arduino be the controller, and use the power supply to distribute power. That relay is drawn oddly... the relay should "break" the hot line, and the N.O. should pass the power to the COM when the relay is actuated...

1 Like

I'm guessing the current will depend on the device (phones, speaker) you plug in.

The problem isn't the lock, all the ardunio is powering is the lock and the mp3 player.

The issue is that it is reading the signals from the esp 32 weirdly, those 4 yellow cables going into A0 to A3 are coming from an ESP32 and reading a short pulse, but reading it incorrectly

This circuit and code actually works if I use microswitches but I'm trying to keep the inputs within the 'pirate ship' theme, hence, switches wont really work.

Hi John, the mp3 works fine, that's not the issue, it's the Arduino reading the HIGH pulses from my ESP32 incorrectly.

I might try connecting relays to the VCC but I don't think there will be enough current :frowning:

Remove the mp3, lock and relay, then test the touch pads alone, and use Serial.print() to "see" the relay/lock/mp3.

Show us this schematic, please. I have a suspicion you have a missing ground reference between the ESP and the Uno.

1 Like

Yes, you could be correct. I had a feeling I was neglecting something fundemental regarding physics but I had done pullup resistor tutorials that worked using 5v from arduino pins so I assumed it would work here.

Here is the wiring, (typo in the bottom left it says white is connected to GPIO 36 when it is actually GPIO 5)

Any advice on how to create this ground reference? Im assuming resistors?

Will try this, but, the lock I'm using is a fail secure lock that only needs 12v momentarily to disengage the latch, do you think I'm using a maglock that needs continous current? Maybe I am misunderstanding you

it depends how the esp32 and uno are powered.
Check with a meter to ensure the potentials arent different, then just connect the grounds together.
Provided you dont attach a usb you can also power the esp32 from the uno 3v3.

Thank you for the suggestion. I am powering them both from usb through my laptop, do you think this could be causing issues?

based on your drawing, not likely. That also should mean your ground is the same for the digitals, but I'd add a wire from the ground of the ESP to the ground of the Uno, just to be sure. If that doesn't solve your triggering issues in this test case, then please show us the code that's running on the Uno to generate the test pulse.

1 Like

I would see this as a debounce issue (with one push, thousands of electrical activities are present between finger and copper). The method of debounce is... when your touch is first detected, the button is ignored for a given length of time. (link)

Could be but im running the bounce library on the arduino, and ive set up the code on the esp to read a "before and after" capacitance and only trigger if the after was lower than the tolerance and the before was higher. This updates every 100ms.

I believe this code is immune to bounce but im also prepared to be wrong on this

The ESP32 is creating the pulse and it is being read by the Arduino.

I grounded both boards and blimey, that seems to have worked! I would have thought the usb on the ESP would have grounded it properly but i guess not.

1 Like

Now, back to your real questions, I guess. But it's good to have a working test path, without the touch issues.

Without the direct ground connection you would have been getting a lot of noise.

It's a bit of a fudge, because I suspect doing this can also introduce ground loops, though I've never looked at what's done onboard a laptop for the ground side of the USB connections. I'd be leery of doing anything significant with two USB connections to the same electrical environment without sniffing with an oscilloscope to see what's going on.

I'd be AMAZED if the USB gounds were not at the same potential and all connected to the same motherboard 0V rail.
However its easy to check by measuring the resistance between them with a meter.

We'd both be amazed at that; but in this instance, if the issue truly went away, it may indicate a problem with the OP's computer, or more likely the USB cables. That's not so rare.

2 Likes