earlephilhower / ESP8266Audio

Arduino library to play MOD, WAV, FLAC, MIDI, RTTTL, MP3, and AAC files on I2S DACs or with a software emulated delta-sigma DAC on the ESP8266 and ESP32
GNU General Public License v3.0
2.05k stars 435 forks source link

Library does not work well with HTTP client being used. #328

Closed timothydillan closed 3 years ago

timothydillan commented 4 years ago

Hi there.

I'm currently making an RFID project where when an RFID tag is scanned, a GET request will be sent to my website, and data in JSON format will be retrieved from the response. This, however, interrupts the ESP8266Audio library.

#include <ESP8266WiFi.h>
#include <ArduinoJson.h>        // version 6.13
#include <SPI.h>
#include <RFID.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include "AudioFileSourceICYStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2SNoDAC.h"
#include <WiFiClientSecureBearSSL.h>
#include "Tone.h"
#include <google-tts.h>

TTS tts;

const uint8_t fingerprintVPS[20] = {0xf3,0xa1,0x3f,0x8c,0x76,0xc3,0xa6,0xac,0x00,0x68,0x8a,0x4f,0xbf,0x0d,0x4d,0x49,0x40,0xca,0x56,0x95};

LiquidCrystal_I2C lcd(0x3F, 20, 4); 

#define SS_PIN D3
#define RST_PIN D4
#define Buzzer D8

RFID rfid(SS_PIN, RST_PIN);

std::unique_ptr<BearSSL::WiFiClientSecure>klien(new BearSSL::WiFiClientSecure);

void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string)
{
  const char *ptr = reinterpret_cast<const char *>(cbData);
  (void) isUnicode; 
  char s1[32], s2[64];
  strncpy_P(s1, type, sizeof(s1));
  s1[sizeof(s1)-1]=0;
  strncpy_P(s2, string, sizeof(s2));
  s2[sizeof(s2)-1]=0;
  Serial.printf("METADATA(%s) '%s' = '%s'\n", ptr, s1, s2);
  Serial.flush();
}

void StatusCallback(void *cbData, int code, const char *string)
{
  const char *ptr = reinterpret_cast<const char *>(cbData);
  char s1[64];
  strncpy_P(s1, string, sizeof(s1));
  s1[sizeof(s1)-1]=0;
  Serial.printf("STATUS(%s) '%d' = '%s'\n", ptr, code, s1);
  Serial.flush();
}

void setup() {
  Serial.begin(115200);
  SPI.begin();
  rfid.init();
  delay(10);
  pinMode(Buzzer, OUTPUT);

  Wire.begin(D2, D1);

  lcd.begin();
  lcd.home ();
  Serial.println();

  Serial.print("Connecting to ");
  Serial.println(wifiName);

  lcd.setCursor(1,0);
  lcd.print("Initialization");
  lcd.setCursor(2,1);
  lcd.print("Connecting..."); 

  WiFi.begin(wifiName, wifiPass);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());   //You can get IP address assigned to ESP

  ModeDevice();
}

void ModeDevice()
{
  klien->setFingerprint(fingerprintVPS);

  HTTPClient https;

  Serial.print("Request Link:");
  Serial.println(hostMode);

  https.begin(*klien, hostMode);

  int httpCode = https.GET();            //Send the request
  String payload = https.getString();    //Get the response payload from server

  Serial.print("Response Code:"); //200 is OK
  Serial.println(httpCode);       //Print HTTP return code

  Serial.print("Returned data from Server:");
  Serial.println(payload);    //Print request response payload

  if (httpCode == 200)
  {
    DynamicJsonDocument doc(1024);

   // Parse JSON object
    auto error = deserializeJson(doc, payload);
    if (error) {
      Serial.print(F("deserializeJson() failed with code "));
      Serial.println(error.c_str());
      return;
    }

    // Decode JSON/Extract values
    String responStatus = doc["status"].as<String>();
    String responMode = doc["mode"].as<String>();
    String responKet = doc["ket"].as<String>();

    Serial.println();
    Serial.print("status : ");
    Serial.println(responStatus);

    Serial.print("mode : ");
    Serial.println(responMode);

    Serial.print("ket : ");
    Serial.println(responKet);
    Serial.println("-------------------");
    Serial.println();

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Dismissal System");

    if (responMode == "SCAN"){
      ModeAlat = "SCAN";
      lcd.setCursor(0,1);
      lcd.print("Scan your card>>");

    }else if (responMode == "ADD"){
      ModeAlat = "ADD";
      lcd.setCursor(0,1);
      lcd.print("Add your card>>>");

    }else{
      ModeAlat = "";
      lcd.setCursor(0,1);
      lcd.print(responKet);
    }

  } else { 
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Connection error.");
    Serial.println();
    Serial.println("Koneksi Error");
    lcd.print("                   ");
    delay(1000);
  }

  Serial.println("Closing connection.");

  https.end();

  delay(100);
}

void speak(const char *url) {

  AudioGeneratorMP3 *mp3a;
  AudioFileSourceICYStream *filea;
  AudioFileSourceBuffer *buffa;
  AudioOutputI2SNoDAC *outa;

  bool audioPlaying = true;
  bool hasSetUpAudio = false;

  Serial.println("Start of TTS function.");
  // While there are no audio playing,
  while (audioPlaying) {
    // and if audio hasn't been set up'ed,
    if (!hasSetUpAudio) {
      audioLogger = &Serial;
      filea = new AudioFileSourceICYStream(url);
      filea->RegisterMetadataCB(MDCallback, (void*)"ICY");
      buffa = new AudioFileSourceBuffer(filea, 4096);
      buffa->RegisterStatusCB(StatusCallback, (void*)"buffer");
      outa = new AudioOutputI2SNoDAC();
      mp3a = new AudioGeneratorMP3();
      mp3a->RegisterStatusCB(StatusCallback, (void*)"mp3");
      Serial.println("MP3 begin.");
      mp3a->begin(buffa, outa);
      // and set hasSetUpAudio to true, so that this code won't be executed twice.
      hasSetUpAudio = true;
    }

    static int lastms = 0;

    // While the audio is playing,
    if (mp3a->isRunning()) {
      //Serial.println("MP3 is running.");
      // Check if we've played for a second,
      if (millis() - lastms >= 1000) {
        lastms = millis();
        // and show that we've played for a second.
        Serial.printf("Running for %d ms...\n", lastms);
       }
      // If the mp3 is not looping,
      if (!mp3a->loop()) {
        // stop the MP3.
        mp3a->stop();
      }
    } else {
      // If the audio is not playing, print it and
      // set audioplaying to true.
      Serial.println("MP3 done");
      delay(1000);
      audioPlaying = false;
    }
  }

  delete mp3a, filea, buffa, outa;
  Serial.println("End of TTS function.");
}

void loop() {

  if (!rfid.isCard() && !rfid.readCardSerial()) {
    rfid.halt();
    delay(1000);
    return;
  }

  tone(Buzzer, NOTE_E7, 100);

  Serial.println("Card found");

  String RFID = String(rfid.serNum[0],HEX) +"-"+ String(rfid.serNum[1],HEX) +"-"+ String(rfid.serNum[2],HEX) +"-"+ String(rfid.serNum[3],HEX) +"-"+ String(rfid.serNum[4],HEX);

  Serial.println("RFID: ");
  Serial.print(RFID);

  String host = hostSCAN;
  host += "&rfid=";
  host += RFID;
  HTTPClient https;
  Serial.print("Request Link:");
  Serial.println(host);

  https.begin(*klien, host);

  int httpCode = https.GET();            //Send the GET request
  String payload = https.getString();    //Get the response payload from server

  https.end();
  klien->stop();

  Serial.print("Response Code:"); //200 is OK
  Serial.println(httpCode);       //Print HTTP return code

  Serial.print("Returned data from Server: ");
  Serial.println(payload);    //Print request response payload

  if (httpCode != 200)
    return;

  // some code here where i just store the variables retrieved from the json response

  String ttsString = "Hi " + name;
  Serial.println(ttsString);

  String urlSpeech = tts.getSpeechUrl(ttsString);
  // removes the "s" from https as the library does not support HTTPS streams.
  urlSpeech.remove(4, 1);

  Serial.println(URL);

  speak(URL.c_str());

  //Initializing the RFID again, because if we don't somehow the RFID doesn't scan a second time.
  SPI.begin();
  rfid.init();
}

The problem is, is it'll play once, and the next time I'll scan my RFID tag, it'll just skip to MP3 Done immediately. The TTS link generated is fine.

Any help is appreciated.

roboticboyer commented 3 years ago

You need to execute the below code in the loop to play the mp3 stream

` // While the audio is playing, if (mp3a->isRunning()) { //Serial.println("MP3 is running."); // Check if we've played for a second, if (millis() - lastms >= 1000) { lastms = millis(); // and show that we've played for a second. Serial.printf("Running for %d ms...\n", lastms); } // If the mp3 is not looping, if (!mp3a->loop()) { // stop the MP3. mp3a->stop(); } } else { // If the audio is not playing, print it and // set audioplaying to true. Serial.println("MP3 done"); delay(1000); audioPlaying = false; }

`

timothydillan commented 3 years ago

You need to execute the below code in the loop to play the mp3 stream

` // While the audio is playing, if (mp3a->isRunning()) { //Serial.println("MP3 is running."); // Check if we've played for a second, if (millis() - lastms >= 1000) { lastms = millis(); // and show that we've played for a second. Serial.printf("Running for %d ms...\n", lastms); } // If the mp3 is not looping, if (!mp3a->loop()) { // stop the MP3. mp3a->stop(); } } else { // If the audio is not playing, print it and // set audioplaying to true. Serial.println("MP3 done"); delay(1000); audioPlaying = false; }

`

That code is executed, it's in the speak function and I'm already calling the speak function in the main loop function. I couldn't find a way to fix it, so I just used a HTTP connection instead.