Intereference When Attempting to analogWrite

I am currently attempting to build a program that acts like a theremin of sorts - to quickly summarize, I take input from a voltage divider using a photo-resistor and use it to determine the frequency I output to a buzzer via analogWrite. Unfortunately, however, I have come into a snag when attempting to add a mute button to enable/disable sound.

Something about my logic that handles button presses results in light detected by the photo-resistor to be read as button presses. I eventually found that I wasn't using pinMode before my analogWrite call for my buzzer, which fixed that issue yet unfortunately raised another - now, instead of light causing interference with my button handling, button handling (as well as the process of connecting to the cloud, and possibly even writing to terminal) is causing interference with my buzzer, not using the amount of light being detected anymore.

To be clear, I've gone over my actual breadboard hookup and have found no issues with how I've connected things. Disabling the button code causes the project to work correctly, but I wish to have this button work as intended. Provided below are the files being compiled for my firmware:

Main file:

// Include Particle Device OS APIs
#include "Particle.h"

// Include other .h files.
#include "ersatz_theremin.h"

// Let Device OS manage the connection to the Particle Cloud
SYSTEM_MODE(AUTOMATIC);

// Run the application and system concurrently in separate threads
SYSTEM_THREAD(ENABLED);

// Show system, cloud connectivity, and application logs over USB
// View logs with CLI using 'particle serial monitor --follow'
SerialLogHandler logHandler(LOG_LEVEL_INFO);

// Names of pins.
const int V_OUT = A0;
const int TMP36 = A1;
const int BUTTON = D0;
const int BUZZER = D1;
const int RED_LED = D2;
const int GREEN_LED = D3;
const int BLUE_LED = D4;

// Initialize class instances.
ErsatzTheremin ersatzTheremin = ErsatzTheremin();

// setup() runs once, when the device is first turned on
void setup()
{
  // Set up pins.
  pinMode(V_OUT, INPUT_PULLDOWN);
  pinMode(TMP36, INPUT_PULLDOWN);
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(BUZZER, OUTPUT);
  pinMode(RED_LED, OUTPUT);
  pinMode(GREEN_LED, OUTPUT);
  pinMode(BLUE_LED, OUTPUT);
}

// loop() runs over and over again, as quickly as it can execute.
void loop()
{
  ersatzTheremin.main();

}

ersatzTheremin files:

#ifndef ersatz_theremin_h
#define ersatz_theremin_h
#include "Particle.h"

class ErsatzTheremin
{
private:
    // Pins needed for this part of the assignment.
    const int V_OUT = A0;
    const int BUTTON = D0;
    const int BUZZER = D1;

    // Volume level, and reference constants.
    int volume = 128;
    const int MUTED_VOL = 0;
    const int UNMUTED_VOL = 128;

    // Button variables.
    const int BOUNCE_INTERVAL = 500;
    int lastTimeButtonPressed = 0;

public:
    void main();
};

#endif
// Include Particle Device OS APIs
#include "Particle.h"

#include "ersatz_theremin.h"

void ErsatzTheremin::main()
{
    
    // Detect whether button is pressed.
    if (digitalRead(BUTTON) == LOW) {
        Serial.printlnf("BUTTON PRESSED NOW at %d", millis());
        // Detect whether it has been at least the bounce interval since the button was last pressed.
        if (millis() - lastTimeButtonPressed > BOUNCE_INTERVAL) {
            // Mute/unmute via altering volume level.
            if (volume == MUTED_VOL) {
                volume = UNMUTED_VOL;
            } else {
                volume = MUTED_VOL;
            }
        }
        lastTimeButtonPressed = millis();
    }

    // Get reading from photoresistor.
    int photoVoltageReading = analogRead(V_OUT);

    // Send reading from photoresistor to buzzer.
    pinMode(BUZZER, OUTPUT); // ENABLING THIS CAUSES SOME KIND OF INTERFERENCE IN THE BUZZER, DISABLING CAUSES PHOTOSENSOR TO INTERFERE W/ BUTTON AS DESCRIBED
    analogWrite(BUZZER, volume, photoVoltageReading);
    pinMode(BUTTON, INPUT_PULLUP); // Enabling/disabling this doesn't (seem to) make a difference.
}

Not addressing your issue, but some hint about coding practice.

You are hardcoding your V_OUT, BUTTON and BUZZER constants in your main program AND in the "library" and also set the pinMode() in both places.
Both should be avoided.

If you have a "library" using particular pins, you should define which pins you want to use in the main program but give your library class a constructor that takes the pins and stores them locally and then have a ::begin() or ::init() function that performs the pin setup (setting pinMode(), initial pin state and so on).
You also don't want to set pinMode() over and over in your ::main() - once should do.

4 Likes

Your debounce code for the button does not check for the falling edge of the input and instead fires continuously at the debounce rate as long as the button is held on. This causes your volume to continuously toggle between MUTED_VOL and UNMUTED_VOL. When you release the button, the last state of volume will become the final value and there is no way to know which value it will have.

Also, you need to change the type of lastTimeButtonPressed to unsigned.

4 Likes

Thank you so much for the suggestions! Unfortunately, even when commenting out the code inside my if(digitalRead(BUTTON) == LOW) I am still registering button presses from the presence of light somehow. This is what I indicated to be interference between the button and photo-resistor that I am unable to resolve. Even when unplugging the button and its wires from my circuit do I get registered button presses whose frequency is dependent on the intensity of light.

Others have pointed out some of the things I saw too, but I have a new question:

Why are you using INPUT_PULLDOWN with the analogRead pin V_OUT?

I am not sure what that will do since the pull-down is meant to be used with digital inputs. I'm pretty sure Device OS is going change that when the analogRead call is made such that there is no pull down. You do not need a pinMode call for an analog input pin.

I would remove that line of code and if you don't already have it, add an external resistor to ground on that input.

1 Like