Running 2 functions on ESP8266 (Buzzer and display)

I want to run 2 functions at once, those being playing an animation on the oled 0.96 display and playing a sound on the buzzer. How would I go about doing that? I tried looking around but couldn't find anything usefull.

Now it plays the sound then the animation. I want the sound to play while the animation is playing.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <CuteBuzzerSounds.h>
#include "idle.h"
#include "blink.h"
#include "happy.h"
#include "happy2.h"

#define BUZZER_PIN D6
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

const int TouchPin = D7; // Touch sensor pin
const float R1 = 20000.0; // 20k Ohm
const float R2 = 10000.0; // 10k Ohm

// Function to display a single frame
void displayFrame(const uint8_t* frame) {
  display.clearDisplay();
  display.drawBitmap(0, 0, frame, SCREEN_WIDTH, SCREEN_HEIGHT, SSD1306_WHITE);
  display.display();
}

int analogValue = analogRead(A0);
float voltage = analogValue * (3.3 / 1023.0);
float inputVoltage = voltage * ((R1 + R2) / R2);
bool isBlinkPlaying = false;
unsigned long lastTouchCheckTime = 0;
const unsigned long touchCheckInterval = 100; // Check touch sensor every 100 milliseconds

void setup() {
  Serial.begin(115200);
  
  pinMode(TouchPin, INPUT); // Initialize touch sensor pin
  cute.init(BUZZER_PIN);
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 10);
  display.println("Loading animations...");
  display.display();

  randomSeed(analogRead(0)); // Initialize random seed for random delays
}

void playBlinkAnimation() {
  static unsigned long lastBlinkTime = 0;
  static unsigned long blinkDelay = 0;
  static size_t blinkFrameIndex = 0;

  unsigned long currentMillis = millis();

  // Check if it's time to blink
  if (currentMillis - lastBlinkTime >= blinkDelay) {
    lastBlinkTime = currentMillis;
    isBlinkPlaying = true;

    for (size_t i = 0; i < blink_frames_count; ++i) {
      displayFrame(blink_frames[i]);
      delay(blink_frameDelay);
    }

    // Update the blink delay
    blinkDelay = random(5000, 10000); // Random delay between 5000 ms and 10000 ms
  }
}

void playHappyAnimation(const uint8_t* frames[], size_t frameCount) {
  static unsigned long lastBlinkTime = 0;
  static unsigned long blinkDelay = 0;
  static size_t blinkFrameIndex = 0;

  unsigned long currentMillis = millis();

  // Check if it's time to blink
  if (currentMillis - lastBlinkTime) {
    lastBlinkTime = currentMillis;
    isBlinkPlaying = true;
    for (size_t i = 0; i < frameCount; ++i) {
      displayFrame(frames[i]);
      delay(happy_frameDelays[i]);
  }
}
}

void playHappyAnimation2(const uint8_t* frames[], size_t frameCount) {
  static unsigned long lastBlinkTime = 0;
  static unsigned long blinkDelay = 0;
  static size_t blinkFrameIndex = 0;

  unsigned long currentMillis = millis();

  // Check if it's time to blink
  if (currentMillis - lastBlinkTime) {
    lastBlinkTime = currentMillis;
    isBlinkPlaying = true;
    for (size_t i = 0; i < frameCount; ++i) {
      displayFrame(frames[i]);
      delay(happy2_frameDelay);
  }
}}

void loop() {
  unsigned long currentMillis = millis();
  Serial.println("Voltage: ");
  Serial.println(inputVoltage);
  // Check if it's time to check the touch sensor
  if (currentMillis - lastTouchCheckTime >= touchCheckInterval) {
    lastTouchCheckTime = currentMillis;

    // Check if the touch sensor is pressed
    if (digitalRead(TouchPin) == HIGH) {
      Serial.println("Touch sensor activated");
      int melodyChoice = random(3);
      switch (melodyChoice) {
        case 0:
          cute.play(S_HAPPY);
          break;
        case 1:
          cute.play(S_HAPPY_SHORT);
          break;
        case 2:
          cute.play(S_SUPER_HAPPY);
          break;
      }

      // Randomly choose between playing "happy" or "happy2" animation
      if (random(2) == 0) {
        playHappyAnimation(happy_frames, happy_frames_count);
      } else {
        playHappyAnimation2(happy2_frames, happy2_frames_count);
      }
    }
  }

  // Display the idle frame
  displayFrame(idle_frame1);

  // Play the blink animation
  playBlinkAnimation();
}

you need to get rid of all the delays and leverage millis()

see

you might benefit as well from studying state machines. Here is a small introduction to the topic: Yet another Finite State Machine introduction

I tries that, a lot. But no luck. It either make the animation really laggy (blink and happy at once), make the blink be constant, play the sound and 1 frame of the animation, ect.

Look, I already use it for the blinking. Tried it for the problem im having too but nothing.

if (currentMillis - lastBlinkTime >= blinkDelay) {
    lastBlinkTime = currentMillis;
    isBlinkPlaying = true;

Heres where I use it for the happy

void playHappyAnimation(const uint8_t* frames[], size_t frameCount) {
  static unsigned long lastBlinkTime = 0;
  static unsigned long blinkDelay = 0;
  static size_t blinkFrameIndex = 0;

  unsigned long currentMillis = millis();

  // Check if it's time to blink
  if (currentMillis - lastBlinkTime) {
    lastBlinkTime = currentMillis;
    isBlinkPlaying = true;
    for (size_t i = 0; i < frameCount; ++i) {
      displayFrame(frames[i]);
      delay(happy_frameDelays[i]);
  }
}

A big part of your challenge is that you delegate the sound generation to the CuteBuzzerSounds library

for example when you play super happy, that's what the function does

so you have a 50ms delay and hundreds small ones hidden into the bendTones() calls as there is a for loop going from the initFrequency to finalFrequency...

You need to get rid of the library and generate the same sounds without any delay().

But one of the sounds I use is:

case S_HAPPY:
    bendTones(1500, 2500, 1.05, 20, 8);
    bendTones(2499, 1500, 1.05, 25, 8);
    break;

that doesn't have a delay and it still acts like it does. And i also tried with my own tones and that didn't work either.

Im trying again now, ill let you know how it goes!

Tried it with this

void playCatHappyTone() {
  unsigned long startTime = millis();

  tone(buzzerPin, noteC5);
  while (millis() - startTime < 200); // Play C5 for 200 ms
  noTone(buzzerPin);

  startTime = millis();
  tone(buzzerPin, noteG5);
  while (millis() - startTime < 200); // Play G5 for 200 ms
  noTone(buzzerPin);

  startTime = millis();
  tone(buzzerPin, noteC6);
  while (millis() - startTime < 400); // Play C6 for 400 ms
  noTone(buzzerPin);
}

but still nothing

Hi @thespectre ,

Welcome to the forum..

Can't replace one type of sit and spin with another, it's still a sit and spin..

You would typically have to repeatedly call your function to step it to completion..
Each step only take but a wink leaving lots of time for other things..

Maybe something like..


#define  NOTE_G5  783.99  //G5
#define  NOTE_C5  523.25  //C5
#define  NOTE_C6  1046.5  //C6

//returns true when song is finished..
bool playCatHappyTone() {
  //is song finished..
  bool result = false;
  static unsigned long startTime = millis();
  const float notes[] = {NOTE_C5, NOTE_G5, NOTE_C6};
  static byte currentNote = 0;
  static byte toneState = 0;
  static int  toneDuration = 200;
// is it time to do something..
  if (millis()- startTime >= toneDuration){
   if (toneState == 0){
    //remember
    toneState = 1;
    //start the tone
    tone(buzzerPin, notes[currentNote]);
    //reset millis timer..
    startTime = millis();
    //set note duration
    if (currentNote == 2){ 
       toneDuration = 400; } else{
        toneDuration = 200;
       }
   } else {
    //remember
    toneState = 0;
    //stop the tone
    noTone(buzzerPin);
    //next note..
    currentNote++;
    //check for sone done..
    if (currentNote >= sizeof(notes)){
      result = true;//song over..
      currentNote = 0;
    }
   }
  }
}

untested sorry..

good luck.. ~q

Or get a DFPlayer

It does - as I said the bendTones() function has a blocking for loop steeping from the start frequency to the end frequency and delays in between

Also your use of millis

Is not what we mean when we say use millis. You have a blocking while loop that does the same thing as delay(400);

You need to read the tutorials and examples again until you get how they work : there is no blocking while or for loop, just the loop spinning and tests with if it’s time to do something

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