/*!
 * @file  configAmplifier.ino
 * @brief  简易配置蓝牙放大器
 * @details  阅读bluetoothAmplifier.ino示例后，您可能已经成功播放音乐。
 * @n  本示例允许您简单配置播放器：设置音量、设置滤波器、获取远程蓝牙设备地址
 * @n  您可以通过setup函数进行配置，也可以在程序运行时根据loop函数中的代码注释通过串口命令调整配置。
 * @note  串口操作可以直接改为按钮或旋钮等其他操作，以实现真正的蓝牙音箱。
 * @copyright  Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
 * @license  The MIT License (MIT)
 * @author  [qsjhyy](yihuan.huang@dfrobot.com)
 * @version  V1.0
 * @date  2022-01-27
 * @url  https://github.com/DFRobot/DFRobot_MAX98357A
 */
#include <DFRobot_MAX98357A.h>

DFRobot_MAX98357A amplifier;   // 实例化一个对象来控制放大器

/**************************************************************
                      初始化与主循环                          
**************************************************************/

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

  /**
   * @brief 初始化函数
   * @param btName - 创建的蓝牙设备名称
   * @param bclk - I2S通信引脚号，串行时钟(SCK)，也称位时钟(BCK)
   * @param lrclk - I2S通信引脚号，字选择(WS)，即命令(通道)选择，用于切换左右声道数据
   * @param din - I2S通信引脚号，串行数据信号(SD)，用于以二进制补码格式传输音频数据
   * @return 成功返回true，失败返回false
   */
  while( !amplifier.begin(/*btName=*/"bluetoothAmplifier", /*bclk=*/GPIO_NUM_25, /*lrclk=*/GPIO_NUM_26, /*din=*/GPIO_NUM_27) ){
    Serial.println("初始化失败！");
    delay(3000);
  }
  Serial.println("初始化成功！");

  /**
   * @fn reverseLeftRightChannels
   * @brief 反转左右声道，当您发现左右声道播放相反时，可以调用此接口进行调整
   */
  // amplifier.reverseLeftRightChannels();

  /**
   * @brief 设置音量
   * @param vol - 设置音量，范围可设置为0-9
   * @note 5为音频数据的原始音量，不增不减
   */
  amplifier.setVolume(5);

  /**
   * @brief 关闭音频滤波器
   * @note 高通滤波器和低通滤波器将同时关闭，因为它们同时工作，
   * @n    当您设置的范围超出人耳可分辨的值时，视为关闭相应的滤波器
   * @n    滤波器的初始值为bq_type_highpass(2)和bq_type_lowpass(20000)，
   * @n    此时认为滤波器已关闭。
   */
  amplifier.closeFilter();

  /**
   * @brief 打开音频滤波器
   * @param type - bq_type_highpass：开启高通滤波；bq_type_lowpass：开启低通滤波
   * @param fc - 滤波阈值，范围：2-20000
   * @note 例如，设置高通滤波模式且阈值为500，表示滤除500以下的音频信号；高通滤波器和低通滤波器将同时工作。
   */
  amplifier.openFilter(bq_type_highpass, 500);

  /**
   * @brief 获取远程蓝牙设备地址
   * @note 该地址将在模块与远程蓝牙设备配对并基于蓝牙AVRCP协议成功通信后获取。
   * @return 返回存储远程蓝牙设备地址的数组指针
   * @n 当模块未连接到远程设备或基于蓝牙AVRCP协议通信失败时返回None。
   * @n AVRCP（音频视频远程控制配置文件）
   */
  uint8_t * addr = amplifier.getRemoteAddress();
  /**
   * 打印获取到的远程蓝牙设备地址
   * 注意：如果未获取到远程蓝牙地址，将持续等待。
   * 函数实现遵循本示例，您可以根据需要进行更改。
   */
  printRemoteAddress(addr);

  /**
   * @brief 向AVRCP目标发送透传命令
   * @param[in]  tl：事务标签，0到15，连续命令应使用不同的值。
   * @param[in]  key_code：透传命令代码，例如
   * @n            ESP_AVRC_PT_CMD_PLAY：开始播放；
   * @n            ESP_AVRC_PT_CMD_STOP：停止播放；
   * @n            ESP_AVRC_PT_CMD_BACKWARD：播放上一首；
   * @n            ESP_AVRC_PT_CMD_FORWARD：播放下一首；等。
   * @param[in]  key_state：透传命令按键状态，ESP_AVRC_PT_CMD_STATE_PRESSED 或 ESP_AVRC_PT_CMD_STATE_RELEASED
   * @note 此函数可能不太实用，但如果您感兴趣可以尝试。详情请参考：
   * @n    https://github.com/espressif/esp-idf/blob/master/components/bt/host/bluedroid/api/include/api/esp_avrc_api.h
   */
  esp_avrc_ct_send_passthrough_cmd(/*tl=*/0, /*tl=key_code*/ESP_AVRC_PT_CMD_PLAY, /*tl=key_state*/ESP_AVRC_PT_CMD_STATE_PRESSED);

}

void loop()
{
  /**
   * @brief 串口命令的解析与实现，即在程序运行时通过串口命令调用上述配置函数
   * @note  可以在串口输入相应命令来配置放大器，格式：cmd-value（命令-设置值，某些不需要赋值的命令可省略）
   * @n  当前可用命令：
   * @n    设置并开启高通滤波器：如 hp-500
   * @n    设置并开启低通滤波器：如 lp-15000
   * @n    关闭滤波器：如 closeFilter-
   * @n    设置音量：如 vol-5
   * @n    获取远程蓝牙设备地址：如 addr-
   * @n    开始播放：如 start-
   * @n    停止播放：如 stop-
   * @n    播放上一首：如 previous-
   * @n    播放下一首：如 next-
   * @n    其他命令将打印此说明
   * @n  命令的详细含义请参考相应函数的注释。
   */
  parseSerialCommand();
  delay(500);
}

/**************************************************************
                  打印获取到的远程蓝牙地址                      
**************************************************************/

void printRemoteAddress(uint8_t * _addr)
{
  while(NULL == _addr){   // 当获取远程蓝牙地址失败时，将等待直到成功
    Serial.println("请连接远程蓝牙设备！");
    delay(5000);
    _addr = amplifier.getRemoteAddress();   // 再次获取远程蓝牙地址
  }
  Serial.print("远程蓝牙设备地址：");
  for(uint8_t i=0; i<5; i++){
    Serial.print(*_addr, HEX);
    Serial.print("-");
    _addr++;
  }
  Serial.println(*_addr, HEX);
  Serial.println();
}

/**************************************************************
                    串口命令的解析与实现                        
**************************************************************/

void parseSerialCommand(void)
{
  String cmd;   // 保存在串口读取的命令类型
  float value;   // 保存在串口读取的命令值

  /**
   * 命令格式：cmd-value
   * cmd：命令类型
   * value：命令类型对应的设置值，某些命令可以为空
   * 例如：(1) 设置高通滤波器，滤除500以下的音频数据：hp-500
   *      (2) 关闭滤波器：closeFilter-
   */
  if(Serial.available()){   // 检测是否有可用的串口命令
    cmd = Serial.readStringUntil('-');   // 读取指定的终止符字符串，用于切割和识别串口命令，后续相同类型不再重复

    if(cmd.equals("hp")){   // 判断是否为设置高通滤波器的命令类型
      Serial.println("正在设置高通滤波器...\n");
      value =Serial.parseFloat();   // 解析字符串并返回浮点数
      /**
       * @brief 打开音频滤波器
       * @param type - bq_type_highpass：开启高通滤波；bq_type_lowpass：开启低通滤波
       * @param fc - 滤波阈值，范围：2-20000
       * @note 例如，设置高通滤波模式且阈值为500，表示滤除500以下的音频信号；高通滤波器和低通滤波器可同时工作。
       */
      amplifier.openFilter(bq_type_highpass, value);

    }else if(cmd.equals("lp")){   // 判断是否为设置低通滤波器的命令类型
      Serial.println("正在设置低通滤波器...\n");
      value = Serial.parseFloat();
      amplifier.openFilter(bq_type_lowpass, value);

    }else if(cmd.equals("closeFilter")){   // 判断是否为关闭滤波器的命令类型
      Serial.println("正在关闭滤波器...\n");
      /**
       * @brief 关闭音频滤波器
       */
      amplifier.closeFilter();

    }else if(cmd.equals("vol")){   // 判断是否为设置音量的命令类型
      Serial.println("正在设置音量...\n");
      value = Serial.parseFloat();
      /**
       * @brief 设置音量
       * @param vol - 设置音量，范围可设置为0-9
       * @note 5为音频数据的原始音量，不增不减
       */
      amplifier.setVolume(value);

    }else if(cmd.equals("addr")){   // 判断是否为获取远程蓝牙地址的命令类型
      /**
       * @brief 获取远程蓝牙地址
       * @note 该地址将在模块与远程蓝牙设备配对并基于蓝牙AVRCP协议成功通信后获取。
       * @return 返回存储远程蓝牙设备地址的数组指针
       * @n 当模块未连接到远程设备或基于蓝牙AVRCP协议通信失败时返回None。
       * @n AVRCP（音频视频远程控制配置文件）
       */
      uint8_t * addr = amplifier.getRemoteAddress();
      /**
       * 打印获取到的远程蓝牙设备地址
       * 注意：如果未获取到远程蓝牙地址，将持续等待。
       * 函数实现遵循本示例，您可以根据需要进行更改。
       */
      printRemoteAddress(addr);

    }else if(cmd.equals("start")){   // 判断是否为开始播放的命令类型
      Serial.println("开始播放...\n");
      /**
       * @brief 向AVRCP目标发送透传命令
       * @param[in]       tl：事务标签，0到15，连续命令应使用不同的值。
       * @param[in]       key_code：透传命令代码，如 ESP_AVRC_PT_CMD_PLAY、ESP_AVRC_PT_CMD_STOP 等。
       * @param[in]       key_state：透传命令按键状态，ESP_AVRC_PT_CMD_STATE_PRESSED 或 ESP_AVRC_PT_CMD_STATE_RELEASED
       * @note 此函数可能不太实用，但如果您感兴趣可以尝试。详情请参考：
       * @n    https://github.com/espressif/esp-idf/blob/master/components/bt/host/bluedroid/api/include/api/esp_avrc_api.h
       */
      esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED);

    }else if(cmd.equals("stop")){   // 判断是否为停止播放的命令类型
      Serial.println("停止播放...\n");
      // 同上
      esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_STOP, ESP_AVRC_PT_CMD_STATE_PRESSED);

    }else if(cmd.equals("previous")){   // 判断是否为播放上一首的命令类型
      Serial.println("播放上一首...\n");
      // 同上
      esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_BACKWARD, ESP_AVRC_PT_CMD_STATE_PRESSED);

    }else if(cmd.equals("next")){   // 判断是否为播放下一首的命令类型
      Serial.println("播放下一首...\n");
      // 同上
      esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_FORWARD, ESP_AVRC_PT_CMD_STATE_PRESSED);

    }else{   // 未知命令类型
      Serial.println("帮助：\n \
      当前可用命令（格式：cmd-value）：\n \
        设置并开启高通滤波器：如 hp-500\n \
        设置并开启低通滤波器：如 lp-15000\n \
        关闭滤波器：如 closeFilter-\n \
        设置音量：如 vol-5.0\n \
        获取远程蓝牙设备地址：如 addr-\n \
        开始播放：如 start-\n \
        停止播放：如 stop-\n \
        播放上一首：如 previous-\n \
        播放下一首：如 next-\n \
      更多命令详情请参考本示例的代码注释。\n");
    }
    while(Serial.read() >= 0);   // 清除串口中剩余的数据
  }
}
