На теплоходе музыка играет (mp3 плеер)

Тема в разделе "Глядите, что я сделал", создана пользователем OldKryptos, 12 ноя 2023.

  1. OldKryptos

    OldKryptos Гик


    Решил я этим летом на свой морской старт «Земля Санникова» поставить корабельный гудок (тифон). Системы, типа - баллон со сжатым воздухом плюс раструб, рассматривать не стал, в силу громоздкости, трудоёмкости и тяжеловесности. Да и скучно, городить такую махину ради одного гудка.
    MP3 плеер – идеальный вариант для такой цели. В сети достаточно гудков на любой вкус, качай - не хочу. Но, тут уже другая проблема. Избыточность устройства. Плеер может больше, чем просто гудеть.
    В сообществах Ардуино часто встречаются «умные» часы, умеющие «проговаривать» текущее время. Да я и сам, тоже имею в активе проект «говорящей» ракеты «Искатель». Она умеет приветствовать капитана корабля и сообщать ему высоту последнего полёта словами. «А почему бы, не проигрывать на корабле музыку?» - подумал я, поражённый своей догадливостью. Так родился мини-проект «На теплоходе музыка играет».

    Что устройство должно было иметь и уметь по итогу?
    1. Набор сигналов (сейчас их 4 шт.) и возможность проигрывания конкретного сигнала по команде;
    2. Набор музыкальных треков (сейчас их 30шт) и проигрывание их в случайном порядке;
    3. Возможность включения конкретного трека (можно одного);
    4. Регулировка громкости (при настройке в домашних условиях, лучше делать потише);
    5. Если, во время проигрывания музыки, включался гудок, то по окончании его звучания, музыка должна была продолжать играть;
    6. Устройство должно быть достаточно громким.
    Светлые головы, которые изобрели плеер и написали библиотеки, во многом позаботились о нас. Первые пять пунктов, по отдельности, легко реализовывались через управление устройством, посредством библиотеки DFRobotDFPlayerMini. Зная номер файла варианта гудка или музыки, можно было запустить его командой с Ардуино. Встроенный рандом позволял проигрывать музыку в случайном порядке, но всё в месте, это не работало. Точнее работало, но не так как хотелось.

    Согласно DFRobotDFPlayerMini, у плеера есть две особые папки, которые имеют свои свойства, это – ADVERT и MP3, остальные именуются числами (01, 02, …). Есть специальная команда, которая позволяет при проигрывании музыки делать врезки из папки ADVERT. То есть достаточно вложить файлы гудков в ADVERT, и проблема решена? Музыка играет, прогудел, играет дальше? Да, музыка играет, но рандом дергает треки из всех папок подряд, в том числе и из ADVERT. Избежать этого методами библиотеки не получилось, изучение мануала плеера, тоже идей не подкинуло. Ну, с другой стороны, кодер я никакой, может просто не тот уровень, чтобы разобраться на уровне системных команд плеера.

    В результате сделал два метода, которые позволяют врезать сигналы гудков в музыку. При этом, музыка случайно проигрывается только из конкретной папки. В этих методах есть свой недостаток, который, возможно, и является причиной того, что похожих методов нет в библиотеке DFRobotDFPlayerMini. Для их работы нужно заранее знать время проигрывания трека. Для этого я использую два массива, в которые вручную забиты значения в миллисекундах для гудков Guti[] и музыкальных файлов trti[]. Еще один момент, первое значение массива trti[] должно быть небольшим (1000). Это, как-бы пустой трек - "тишина".

    На тот случай если, у кого то, возникнет похожая история, прилагаю код. Контроллер в схеме специализирован под плеер и работает, как ведомое устройство в сцепке I2C. Ведущая Ардуино получает команды по RC и передает их контроллеру плеера.
    В коде есть метод key_power(), он к функциональности плеера отношения не имеет. Используется для управления других устройств.

    Схема
    pleer-sh.jpg

    Внешний вид
    IMG_1239.jpg

    Оформление колонок
    IMG_1260.jpg

    Пример файлов на карте
    pleer-sd.jpg
     
    Последнее редактирование: 13 ноя 2023
  2. OldKryptos

    OldKryptos Гик

    Код (C++):

    /* ----------------------------------------- */
    /* ---------------- гудок ------------------ */
    /* -------- автор: Владимир Коранов -------- */
    /* ----------------------------------------- */
    /* ------------- pleer-Gudok --------------- */
    /* ------- author: Vladimir Koranov -------- */
    /* ----------------------------------------- */
    /*            scetch name: pleer-Gudok       */
    /* ----------------------------------------- */
    #include "SoftwareSerial.h"
    #include "DFRobotDFPlayerMini.h"
    #include <Wire.h>

    // Дебаггер- раздефайнить или задефайнить первую строку для использования
    //#define DEBUG_ENABLE
    #ifdef DEBUG_ENABLE
    #define DEBUG(x) Serial.println(x)
    #else
    #define DEBUG(x)
    #endif

    /*Примеры ситаксиса сообщения дебаггера
      //DEBUG((String)"val_move = " + val_move +  " val_turn = " + val_turn + " val_kran_rot = " + val_kran_rot);
      //DEBUG((String)"val_L = " + val_L);
    */


    /* константы */
    #define out1 7      // выход3
    #define out2 8      // выход4

    #define d_Rx 10     // плеер-RX
    #define d_Tx 11     // плеер-TX

    #define sizeTrTi 31 // размер массива с таймингом треков
    #define sizeGuTi 5  // размер массива с таймингом гудков

    unsigned long timeComanda;            // время шага для таймаута по командам

    int volumeSp;                         // текущий уровень громкости музыки, ака плеера
    int volumeSp_is;                      // для контроля изменения громкости в методах
    int volGudok;                         // уровень громкости гудков

    boolean set_on;                       // признак того, что поступила команда
    boolean igrai;                        // флаг того, что играет музыка
    int comanda_id;                       // команда пришедшая по I2C
    int comanda_is;                       // для контроля команд I2C
    int numTrack;                         // номер звучащего трека
    unsigned long timeTrack_is;           // глобальное время окончания звучания текущего трека
    long valGSCh;                         // значение ГСЧ

    // тайминг музыки
    unsigned long trti [sizeTrTi] { 1000,
               198000, 111000, 167000, 260000, 169000, 108000, 156000, 148000, 162000, 169000,
               155000, 118000, 104000, 157000, 169000, 164000, 85000,  216000, 107000, 102000,
               20000,  265000, 195000,  82000, 109000, 139000, 265000, 559000, 242000, 204000
    };

    // тайминг гудков
    unsigned long Guti [sizeGuTi] { 1000, 7500, 2500, 7500, 6500 }; // реальное время + 500

    /* объекты */
    SoftwareSerial mySoftwareSerial(d_Rx, d_Tx);
    DFRobotDFPlayerMini myDFPlayer;

    /* --------------------------------------------------------------------------------*/
    void setup() {
      Wire.begin(44);   // подключаемся к шине I2C как ведомый
      Wire.onReceive(PrishlaComanda);

      /* выходы на электронные ключи доп.эффектов (не для плеера) */
      pinMode (out1, OUTPUT);             // выход1 (на электронный ключ)
      pinMode (out2, OUTPUT);             // выход2 (-//-)
      digitalWrite (out1, LOW);
      digitalWrite (out2, LOW);

      /*Остальное*/
      set_on = LOW;
      igrai = LOW;
      timeComanda = 0;
      timeTrack_is = 0;
      volumeSp = 2;
      volGudok = 2;
      //payer
      mySoftwareSerial.begin(9600);

    #ifdef DEBUG_ENABLE
      Serial.begin (9600);
    #endif

      if (!myDFPlayer.begin(mySoftwareSerial)) {  //Use softwareSerial to communicate with mp3.
        Serial.println(F("Unable to begin:"));
        Serial.println(F("1.Please recheck the connection!"));
        Serial.println(F("2.Please insert the SD card!"));
        while (true) {
          delay(0); // Code to compatible with ESP8266 watch dog.
        }
      }
    }

    /* --------------------------------------------------------------------------------*/
    void loop() {

      /* if ( volumeSp != analogRead(a_gromk) / 33 ) {                         // --- громкость в ручном режиме (для отладки) --- ///
         volumeSp = analogRead(a_gromk) / 33;
         /DEBUG((String)"volumeSp = " + volumeSp);
        }*/


      if (comanda_is != comanda_id) {                                         // --- прием команды по IIS --- ///
        comanda_is = comanda_id;
        comanda_id = 0; set_on = HIGH;
      }

      switch ( comanda_is ) {                                                 // --- функции f3 --- ///
        //case 10: break;                                                             // резерв (можно задействовать подо что-то)
        case 11: key_power( out1 ); break;                                            // эл.ключ 1
        case 12: key_power( out2 ); break;                                            // эл.ключ 2
      }
        switch ( comanda_is ) {                                                 // --- установка уровня громкости --- ///
        case 7: volumeSp++; volumeSp = constrain(volumeSp, 1, 30); volGudok = volumeSp; break;                                        // тише
        case 8: volumeSp--; volumeSp = constrain(volumeSp, 1, 30); volGudok = volumeSp; break;                                        // громч
      }

      //if ( comanda_is != 0 ) DEBUG((String)"comanda_is = " + comanda_is);

      if ( set_on == HIGH ) {
        set_on = LOW;                                                         // --- исполнение команд для плеера --- ///
        switch ( comanda_is ) {
          case 1: gogo_gudok( 2, igrai ); break;                             // ревун
          case 2: gogo_gudok( 1, igrai ); break;                             // гудок
          case 3: gogo_gudok( 3, igrai ); break;                             // склянки
          case 4: timeTrack_is = 0; numTrack = 0; gogo_track(&numTrack, 1, 0); igrai = HIGH; break;       // музыка
          case 5: myDFPlayer.reset(); timeTrack_is = 0; igrai = LOW; break;                               // музыка выкл
          case 6: myDFPlayer.volume(volumeSp); myDFPlayer.playLargeFolder(3, 3); break;                   // воспроизвести трек № XX
        }
      }

      if (igrai == HIGH) {
        gogo_track(&numTrack, 1, 0);
      }
    }

    /* ------------------------------------------------------------------------- */
    /* ------------------------- Подпрограммы и методы ------------------------- */
    /* ------------------------------------------------------------------------- */

    /* --- чтение команды IIC --------------------------------------*/
    void PrishlaComanda() {
      comanda_id = Wire.read();
    }

    /* --- оператор гудков -----------------------------------------*/
    void  gogo_gudok(int track,  int igra ) {
      unsigned long timeReclam_on;
      myDFPlayer.volume(volGudok);
      if ( igra == HIGH ) {                                       //врезка сигнала в музыкальый трек
        myDFPlayer.advertise(track);
        timeTrack_is = timeTrack_is + Guti[track];                //плюсуем к глобальному времени игры трека время сигнала
        delay( Guti[track] - 200 ); myDFPlayer.volume(volumeSp);  //без задержки работает криво
        return;
      }
      DEBUG((String)"volumeSp = " + volumeSp + "; track = " + track);
      myDFPlayer.volume(volumeSp);
      myDFPlayer.playLargeFolder(1, track);                       //сигнал без проигрывания музыки
    }

    /* --- оператор треков -----------------------------------------*/
    // по номеру трека берет его время проигрывания, и по прошествии времени, начинает новую композицию
    // использует третью папку на карте
    void  gogo_track(int* track, int mode, int new_track) {
      DEBUG((String)"GOGO");
      // --- проверка тайминга текущего трека --- //
      if ( millis() < timeTrack_is + trti[ *track ] ) {
        if ( volumeSp_is != volumeSp ) {                // если время не вышло, то корректируем только громкость, если она изменилась
          volumeSp_is = volumeSp;
          myDFPlayer.volume(volumeSp);
          return;
        }
        return;
      }
      DEBUG((String)"GOGO2");
      // --- Eсли время текущего трека закончилось, то: --- //
      switch (mode) {

        // --- запускается следующий трек --- //
        case 0:
          if ( *track < 1 || *track >= sizeTrTi ) *track = 1;
          else *track = *track + 1;
          break;

        // --- запускается случайный трек --- //
        case 1:
          randomSeed(micros());
          valGSCh = random(1, sizeTrTi);
          while (valGSCh == *track) {                 // запрет на повторное воспроизведение предыдущего трека
            valGSCh = random(1, sizeTrTi);
          }
          DEBUG((String)"New_misic = On; valGSCh = " + valGSCh + "valGSCh = " + valGSCh);
          *track = valGSCh;
          break;

        // --- запускается указанный трек --- //
        case 2:
          *track = new_track;
          break;
      }

      timeTrack_is = millis();
      DEBUG((String)"New_misic = " + numTrack + ";");
      myDFPlayer.volume(volumeSp); myDFPlayer.playLargeFolder(3, numTrack);
    }

    /* --- управление электронным ключом ---------------------------*/
    void key_power(int key_num ) {
      digitalWrite (key_num, HIGH);
      delay(2000);
      digitalWrite (key_num, LOW);
    }
     
     
    Un_ka нравится это.
  3. OldKryptos

    OldKryptos Гик

    Перекинул управление подъемником на контроллер бортового плеера, так как на основном контроллере кончились ноги.