Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pm:prj2025:cmoarcas:gheorghe.petrica [2025/05/21 10:35]
gheorghe.petrica [Software Design]
pm:prj2025:cmoarcas:gheorghe.petrica [2025/05/30 09:55] (current)
gheorghe.petrica [Rezultate Obţinute]
Line 52: Line 52:
  
 === Diagrama Bloc pentru casetofon === === Diagrama Bloc pentru casetofon ===
-{{:​pm:​prj2025:​cmoarcas:​arduino_uno.png?700|}}+{{:​pm:​prj2025:​cmoarcas:​ImperiumBT.png?700|}}
  
 === Schema electrica === === Schema electrica ===
Line 59: Line 59:
 ===== Software Design ===== ===== Software Design =====
  
-<code C> +== CDC Emulator ==
-// #define DEBUG +
-#define CDC_PREFIX1 0x53 +
-#define CDC_PREFIX2 0x2C+
  
-#define CDC_END_CMD 0x14 +Pentru a putea trimite orice fel de semnal audio catre casetofonul auto, vom folosi un Arduino Uno, iar cu ajutorul interfetei SPI vom trimite o secvente de date catre casetofon. In lipsa acestei secvente, casetofonul in momentul in care este aleasa optiunea de redare de pe CD, va genera o eroare de tipul **NO CD CHANGER** chiar daca pe inputul audio este trimis semnal.
-#define CDC_PLAY 0xE4 +
-#define CDC_STOP 0x10 +
-#define CDC_NEXT 0xF8 +
-#define CDC_PREV 0x78 +
-#define CDC_SEEK_FWD 0xD8 +
-#define CDC_SEEK_RWD 0x58 +
-#define CDC_CD1 0x0C +
-#define CDC_CD2 0x8C +
-#define CDC_CD3 0x4C +
-#define CDC_CD4 0xCC +
-#define CDC_CD5 0x2C +
-#define CDC_CD6 0xAC +
-#define CDC_SCAN 0xA0 +
-#define CDC_SFL 0x60 +
-#define CDC_PLAY_NORMAL 0x08+
  
-#define MODE_PLAY 0xFF +Folosim urmatoarele define-uri pentru a indica fiecare comanda suportata de casetofon:​ 
-#define MODE_SHFFL 0x55 +  #define CDC_PREFIX1 0x53 
-#define MODE_SCAN 0x00+  #define CDC_PREFIX2 0x2C 
 +  #define CDC_END_CMD 0x14 
 +  #define CDC_PLAY 0xE4 
 +  #define CDC_STOP 0x10 
 +  #define CDC_NEXT 0xF8 
 +  #define CDC_PREV 0x78 
 +  #define CDC_SEEK_FWD 0xD8 
 +  #define CDC_SEEK_RWD 0x58 
 +  #define CDC_CD1 0x0C 
 +  #define CDC_CD2 0x8C 
 +  #define CDC_CD3 0x4C 
 +  #define CDC_CD4 0xCC 
 +  #define CDC_CD5 0x2C 
 +  #define CDC_CD6 0xAC 
 +  #define CDC_SCAN 0xA0 
 +  #define CDC_SFL 0x60 
 +  #define CDC_PLAY_NORMAL 0x08 
 +  ​#define MODE_PLAY 0xFF 
 +  #define MODE_SHFFL 0x55 
 +  #define MODE_SCAN 0x00 
 +  uint8_t cd, tr, mode; 
 +  unsigned long prevMillis = 0; 
 +   
 +Functia urmatoare **send_package()** trimite un pachet de cate 8 octeti pe SPI, cu intarziere pe fiecare transfer: 
 +  void send_package(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4, uint8_t c5, uint8_t c6, uint8_t c7) { 
 +    uint8_t data[8] = {c0, c1, c2, c3, c4, c5, c6, c7}; 
 +    for (int i = 0; i < 8; i++) { 
 +      SPDR = data[i]; 
 +      while (!(SPSR & (1 << SPIF))); 
 +      delayMicroseconds(874);​ 
 +    } 
 +  }
  
-#include <​Arduino.h>​ +Initializam ​SPI, dupa cum urmeazaastfel incat sa ruleze cu o viteza de transfer ​de 62.5kHz 
-#include <SPI.h> +  void spi_init() { 
- +    DDRB |(<< PB3(1 << PB5); 
-volatile long prevMillis = 0; +    DDRB &= ~(1 << PB4); 
- +    SPCR = (1 << SPE(1 << MSTR(1 << SPR1(1 << SPR0); 
-uint8_t cd; +    SPSR &= ~(1 << SPI2X); 
-uint8_t tr; +  }
-uint8_t mode; +
- +
-void send_package(uint8_t c0uint8_t c1uint8_t c2, uint8_t c3, uint8_t c4, uint8_t c5, uint8_t c6, uint8_t c7) { +
-  SPI.beginTransaction(SPISettings(62500,​ MSBFIRST, SPI_MODE1));​ +
-  SPI.transfer(c0); +
-  delayMicroseconds(874);​ +
-  SPI.transfer(c1);​ +
-  ​delayMicroseconds(874);​ +
-  SPI.transfer(c2);​ +
-  delayMicroseconds(874);​ +
-  SPI.transfer(c3);​ +
-  delayMicroseconds(874);​ +
-  SPI.transfer(c4);​ +
-  delayMicroseconds(874);​ +
-  SPI.transfer(c5);​ +
-  delayMicroseconds(874);​ +
-  SPI.transfer(c6);​ +
-  delayMicroseconds(874);​ +
-  SPI.transfer(c7);​ +
-  SPI.endTransaction();​ +
-+
- +
-void setup() { +
-  ​cd ​= 1+
-  tr = 1; +
-  mode = MODE_PLAY;​ +
- +
-  #ifdef DEBUG +
-  Serial.begin(9600)+
-  #endif +
- +
-  delay(1000); // wait for radio to boot +
- +
-  // Init SPI for Arduino Uno (uses default pins) +
-  SPI.begin(); // uses hardware SPI: SCK(13), MISO(12), MOSI(11) +
- +
-  send_package(0x74, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0x8F, 0x7C); // idle +
-  delayMicroseconds(10000)+
-  send_package(0x34, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xFA, 0x3C); // load disc +
-  delayMicroseconds(100000); +
-  ​send_package(0x74, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0x8F, 0x7C); // idle +
-  ​delayMicroseconds(10000);​ +
- +
-  #ifdef DEBUG +
-  Serial.println("​Sent idle/​load/​idle commands"​);​ +
-  #endif +
-}+
  
-void loop() { +In functia **setup()** initializam valorile initiale pentru disc, mod de redare, piesa, precum si interfata SPI. Dupa care trimitem o secventa de comenzi pentru //load// si //idle// 
-  ​if ((millis() - prevMillis) > 50) { +  ​void setup() { 
-    ​//                  disc      trk         ​min ​ sec +    ​cd = 1; 
-    ​send_package(0x34,​ 0xBF ^ cd, 0xFF ^ tr, 0xFF, 0xFF, mode, 0xCF, 0x3C)+    tr = 1
-    ​prevMillis ​millis()// reset timer+    ​mode MODE_PLAY;
     #ifdef DEBUG     #ifdef DEBUG
-    Serial.println("​Sent ​packet");+    ​Serial.begin(9600);​ 
 +    #endif 
 +    delay(1000);​ 
 +    spi_init();​ 
 +    send_package(0x74,​ 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0x8F, 0x7C); // idle 
 +    delayMicroseconds(10000);​ 
 +    send_package(0x34,​ 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xFA, 0x3C); // load disc 
 +    delayMicroseconds(100000);​ 
 +    send_package(0x74,​ 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0x8F, 0x7C); // idle 
 +    delayMicroseconds(10000);​ 
 +    #ifdef DEBUG 
 +    ​Serial.println("​Sent ​idle/​load/​idle commands");
     #endif     #endif
   }   }
-} 
-// #define DEBUG 
-#define CDC_PREFIX1 0x53 
-#define CDC_PREFIX2 0x2C 
  
-#define CDC_END_CMD 0x14 +In continuare tot pe Arduino, in functia **loop()** vom trimite la fiecare 50ms cate un pachet care contine discul, piesa si modul de redare pentru a mentine conexiunea activa cu casetofonul auto ales: 
-#define CDC_PLAY 0xE4 +  void loop() { 
-#define CDC_STOP 0x10 +    if ((millis() - prevMillis) > 50) { 
-#define CDC_NEXT 0xF8 +      ​send_package(0x34,​ 0xBF ^ cd, 0xFF ^ tr, 0xFF, 0xFF, mode, 0xCF, 0x3C); 
-#define CDC_PREV 0x78 +      ​prevMillis = millis();
-#define CDC_SEEK_FWD 0xD8 +
-#define CDC_SEEK_RWD 0x58 +
-#define CDC_CD1 0x0C +
-#define CDC_CD2 0x8C +
-#define CDC_CD3 0x4C +
-#define CDC_CD4 0xCC +
-#define CDC_CD5 0x2C +
-#define CDC_CD6 0xAC +
-#define CDC_SCAN 0xA0 +
-#define CDC_SFL 0x60 +
-#define CDC_PLAY_NORMAL 0x08+
  
-#define MODE_PLAY 0xFF +      ​#ifdef DEBUG 
-#define MODE_SHFFL 0x55 +      ​Serial.println("​Sent packet"​);​ 
-#define MODE_SCAN 0x00+      #endif 
 +    } 
 +  }
  
-#include <​Arduino.h>​ +== Bluetooth Receiver ==
-#include <​SPI.h>​+
  
-volatile long prevMillis = 0;+In urmatoarele bucati de cod voi descrie cum am folosit un **ESP32** pentru a deveni un receiver audio prin interfata Bluetooth a acestuia si sa redirectionez semnalul audio prin interfata I2S catre DAC extern (e.g. CS4344)
  
-uint8_t cd; +Definim in continuare pinii folositi pentru interfata I2S, butoanele de play/pause pe care le vom folosi, pinii SPI folositi pentru ecranul TFT, care va folosi biblioteca Adafruit_ST7735,​ precum si variabilele folosite pentru debounce: 
-uint8_t tr; +  #​define I2S_BCLK ​   26 
-uint8_t mode;+  #​define I2S_LRCK ​   25 
 +  #define I2S_DATA ​   22 
 +  #define I2S_MCLK ​   3  // sau I2S_PIN_NO_CHANGE
  
-void send_package(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4, uint8_t c5, uint8_t c6, uint8_t c7) { +  // === Butoane === 
-  ​SPI.beginTransaction(SPISettings(62500,​ MSBFIRST, SPI_MODE1));​ +  ​#define BTN_PLAY ​   12 
-  ​SPI.transfer(c0);​ +  ​#define BTN_PAUSE ​  ​14 ​ // redenumit din NEXT
-  delayMicroseconds(874);​ +
-  SPI.transfer(c1);​ +
-  delayMicroseconds(874);​ +
-  SPI.transfer(c2);​ +
-  delayMicroseconds(874);​ +
-  SPI.transfer(c3);​ +
-  delayMicroseconds(874);​ +
-  SPI.transfer(c4);​ +
-  delayMicroseconds(874);​ +
-  SPI.transfer(c5);​ +
-  delayMicroseconds(874);​ +
-  SPI.transfer(c6);​ +
-  delayMicroseconds(874);​ +
-  SPI.transfer(c7);​ +
-  SPI.endTransaction();​ +
-}+
  
-void setup() { +  // === Ecran ST7735 === 
-  ​cd = 1; +  ​#define TFT_CS ​    5 
-  ​tr = 1; +  ​#define TFT_RST ​   4 
-  ​mode = MODE_PLAY;+  ​#define TFT_DC ​    16
  
-  ​#ifdef DEBUG +  ​Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST); 
-  Serial.begin(9600); +  ​BluetoothA2DPSink a2dp_sink;
-  ​#endif+
  
-  ​delay(1000)// wait for radio to boot+  ​String current_song = "Fara melodie";​ 
 +  String last_displayed_song = ""​;
  
-  ​// Init SPI for Arduino Uno (uses default pins) +  ​volatile bool playPressed = false; 
-  ​SPI.begin()// uses hardware SPI: SCK(13), MISO(12), MOSI(11)+  ​volatile bool pausePressed = false;
  
-  ​send_package(0x74,​ 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0x8F, 0x7C)// idle +  ​unsigned long lastDebouncePlay = 0
-  ​delayMicroseconds(10000)+  ​unsigned long lastDebouncePause = 0
-  ​send_package(0x34,​ 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xFA, 0x3C); // load disc +  ​const unsigned long debounceDelay = 150;
-  delayMicroseconds(100000);​ +
-  send_package(0x74,​ 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0x8F, 0x7C); // idle +
-  delayMicroseconds(10000);+
  
-  ​#ifdef DEBUG +  ​unsigned long lastPlayPressTime = 0; 
-  ​Serial.println("​Sent idle/​load/​idle commands"​); +  ​bool waitingForSecondClick = false;
-  #endif +
-}+
  
-void loop() { +Functia urmatoare **metadata_callback()** va fi folosita pentru trimiterea de text pe ecranul TFT: 
-  ​if ((millis() - prevMillis) > 50) { +  ​void metadata_callback(uint8_t id, const uint8_t *text) { 
-    ​//                  disc      trk         ​min ​ sec +    ​if (id == 0x01{ 
-    send_package(0x34, 0xBF ^ cd, 0xFF ^ tr, 0xFF, 0xFF, mode, 0xCF, 0x3C); +      ​current_song ​String((char*)text); 
-    ​prevMillis ​millis(); // reset timer +    ​}
-    #ifdef DEBUG +
-    Serial.println("​Sent packet"​); +
-    ​#endif+
   }   }
-} 
-</code C> 
  
-===== Rezultate Obţinute ===== 
  
-<note tip> +In functia **update_display()** vom scrie momentan numele melodiei curente: 
-Care au fost rezultatele obţinute în urma realizării proiectului vostru+  void update_display() { 
-</​note>​+    if (current_song != last_displayed_song) { 
 +      tft.fillScreen(ST77XX_BLACK);​ 
 +      tft.setTextColor(ST77XX_WHITE);​ 
 +      tft.setTextSize(1);​ 
 +      tft.setCursor(2,​ 10); 
 +      tft.println("​Redare Bluetooth:"​);​ 
 +      tft.setCursor(2,​ 30); 
 +      tft.setTextWrap(true);​ 
 +      tft.setTextSize(1);​ 
 +      tft.println(current_song);​ 
 +      tft.setCursor(2,​ 60); 
 +      tft.setTextSize(1);​ 
 +      tft.println("​Play=D12 | Pause=D14"​);​ 
 +      last_displayed_song = current_song;​ 
 +    } 
 +  }
  
-===== Concluzii =====+Definim urmatoarele 2 functii care vor declansa intreruperile corespunzatoare cand unul din cele 2 butoane este selectat: 
 +  void IRAM_ATTR isr_play() { 
 +    if ((millis() - lastDebouncePlay) > debounceDelay) { 
 +      playPressed ​true; 
 +      lastDebouncePlay ​millis(); 
 +    } 
 +  }
  
-===== Download =====+  void IRAM_ATTR isr_pause() { 
 +    if ((millis() - lastDebouncePause) > debounceDelay) { 
 +      pausePressed ​true; 
 +      lastDebouncePause ​millis(); 
 +    } 
 +  }
  
-<note warning>​ +In functia **setup()** initializam interfata I2S cu un sample rate de 44.1 Mhz si un MCLK de 11.2896 Mhz, precum si pinii pe care ii folosim pentr DAC-ul CS4344. Mai apoi pornim receiver-ul Bluetooth A2DP si initializam ​conexiune '​ESP32_Speaker'​. In final setam butoanele D12 si D14 ca intrari cu rezistenta de pull-up interna, pe care atasam 2 intreruperi. 
-O arhivă ​(sau mai multe dacă este cazul) cu fişierele obţinute în urma realizării proiectului:​ surse, scheme, etcUn fişier README, ​un ChangeLog, un script ​de compilare şi copiere automată ​pe uC crează întotdeauna ​impresie bună ;-).+  void setup() { 
 +    Serial.begin(115200); 
 +    i2s_config_t i2s_config = { 
 +      .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),​ 
 +      .sample_rate = 44100, 
 +      .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,​ 
 +      .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,​ 
 +      .communication_format = I2S_COMM_FORMAT_STAND_I2S,​ 
 +      .intr_alloc_flags = 0, 
 +      .dma_buf_count = 8, 
 +      .dma_buf_len = 64, 
 +      .use_apll = true, 
 +      .tx_desc_auto_clear = true, 
 +      .fixed_mclk = 11289600 
 +    }; 
 +    i2s_pin_config_t pin_config = { 
 +      .mck_io_num = I2S_MCLK, 
 +      .bck_io_num = 26, 
 +      .ws_io_num = 25, 
 +      .data_out_num = 22, 
 +      .data_in_num = I2S_PIN_NO_CHANGE 
 +    }; 
 +    a2dp_sink.set_pin_config(pin_config);​ 
 +    a2dp_sink.set_avrc_metadata_callback(metadata_callback);​ 
 +    a2dp_sink.start("​ESP32_Speaker"​);​ 
 +    tft.initR(INITR_BLACKTAB);​ 
 +    tft.setRotation(1);​ 
 +    update_display();​ 
 +    pinMode(BTN_PLAY,​ INPUT_PULLUP);​ 
 +    pinMode(BTN_PAUSE,​ INPUT_PULLUP);​ 
 +    attachInterrupt(digitalPinToInterrupt(BTN_PLAY),​ isr_play, FALLING); 
 +    attachInterrupt(digitalPinToInterrupt(BTN_PAUSE),​ isr_pause, FALLING); 
 +  } 
 +   
 +In functia **loop()** se executa continuu si verifica daca butoanele D12 si D14 sunt apasate, precum actualizeaza si ecranul TFT: 
 +  void loop() { 
 +    unsigned long now = millis(); 
 +    if (playPressed) { 
 +      playPressed = false; 
 +      if (waitingForSecondClick && (now lastPlayPressTime < doubleClickDelay)) { 
 +        a2dp_sink.next(); 
 +        waitingForSecondClick = false; 
 +      } else { 
 +        lastPlayPressTime = now; 
 +        waitingForSecondClick = true; 
 +      } 
 +    } 
 +    if (waitingForSecondClick && (now - lastPlayPressTime > doubleClickDelay)) { 
 +      a2dp_sink.play();​ 
 +      waitingForSecondClick = false; 
 +    } 
 +    if (pausePressed) { 
 +      pausePressed = false; 
 +      a2dp_sink.pause();​ 
 +    } 
 +    update_display();​ 
 +    delay(50);​ 
 +  }
  
-Fişierele se încarcă pe wiki folosind facilitatea **Add Images or other files**. Namespace-ul în care se încarcă fişierele este de tipul **:​pm:​prj20??:​c?​** sau **:​pm:​prj20??:​c?:​nume_student** (dacă este cazul). **Exemplu:​** Dumitru Alin, 331CC -> **:​pm:​prj2009:​cc:​dumitru_alin**. +===== Rezultate Obţinute ​=====
-</​note>​ +
- +
-===== Jurnal ​=====+
  
 <note tip> <note tip>
-Puteți avea și o secțiune ​de jurnal în care să poată urmări asistentul ​de proiect progresul proiectului.+Rezultatele obtinute au fost destul ​de satisfacatoare,​ am intampinat cateva probleme pe care nu am putut sa le rezolv din cauza DAC-ului ales, deoarece acesta are nevoie ​de un master clock extern care sa fie sincron cu celelalte semnale de pe ele, iar ESP-ul nu este capabil de a oferi corect un astfel de semnal, generand destul de mult zgomot (cred ca un MAX98357 ar fi rezolvat problema zgomotului). Rezultatul final poate fi observat in urmatorul link: 
 +https://​drive.google.com/​file/​d/​1erp1HS01shDhwGrpYLM5Av1lJeFgSRbM/​view?​usp=sharing
 </​note>​ </​note>​
  
 ===== Bibliografie/​Resurse ===== ===== Bibliografie/​Resurse =====
  
-<​note>​ +https://​github.com/​NullString1/​VWCDC 
-Listă cu documente, datasheet-uri,​ resurse Internet folosite, eventual grupate pe **Resurse Software** şi **Resurse Hardware**+https://​schuett.io/​2013/​09/​avr-raspberry-pi-vw-beta-vag-cdc-faker
-</note> +https://itohi.com/​acoustics/​esp32-as-bluetooth-audio/​
- +
-<​html><​a class="​media mediafile mf_pdf"​ href="?​do=export_pdf">​Export to PDF</a></html>+
  
pm/prj2025/cmoarcas/gheorghe.petrica.1747812918.txt.gz · Last modified: 2025/05/21 10:35 by gheorghe.petrica
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0