Differences

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

Link to this comparison view

pm:prj2026:theodor_ioan.buliga:adrian.vancea [2026/05/03 23:09]
adrian.vancea [Journal]
pm:prj2026:theodor_ioan.buliga:adrian.vancea [2026/05/27 08:11] (current)
adrian.vancea [Results]
Line 1: Line 1:
-====== Portable MP3 Player ======+{{:​pm:​prj2026:​theodor_ioan.buliga:​portable_mp3_player.zip|}}====== Portable MP3 Player ======
  
 ===== Introduction ===== ===== Introduction =====
Line 28: Line 28:
  
  
-{{:​pm:​prj2026:​theodor_ioan.buliga:​block_diagram.png?​600|}}+{{:​pm:​prj2026:​theodor_ioan.buliga:​block_diagram.png?​550|}}
  
 <​note>​ <​note>​
Line 54: Line 54:
  
 ==== Schematics ==== ==== Schematics ====
 +The electrical schematic was designed in KiCad, ensuring a modular and portable design.
 +
 +{{:​pm:​prj2026:​theodor_ioan.buliga:​diagrama_electrica.png?​800|}}
 +
 +<​note>​
 +**Hardware Details:**
 +  * **Power:** Managed by the TP4056 module. The battery provides ~3.7V, which powers the Arduino via the VIN pin.
 +  * **Audio:** DAC_R and DAC_L pins from DFPlayer are connected to the Audio Jack for stereo output.
 +  * **Protection:​** A 1kOhm resistor is placed on the UART line between Nano (TX) and DFPlayer (RX) to prevent noise and protect the 3.3V logic of the player.
 +</​note>​
 +
 +==== Pin Mapping ====
 +
 +^ Component ^ Arduino Pin ^ Connection Type ^
 +| OLED Display | A4 (SDA), A5 (SCL) | I2C (Hardware TWI) |
 +| DFPlayer Mini | D8 (TX), D9 (RX) | UART (9600 bps) |
 +| Buttons | D2, D3, D4 | Digital Input (Interrupts) |
 +| Audio Jack | DAC_R, DAC_L, GND | Analog Audio |
 +| Power Switch | VIN | Power Control |
 +
 +
 +=== Exemplu de functionare:​ ===
 +
 +{{:​pm:​prj2026:​theodor_ioan.buliga:​poza_2.jpeg?​600|}}
 +
 +{{:​pm:​prj2026:​theodor_ioan.buliga:​poza_1.jpeg?​600|}}
  
 ===== Software Design ===== ===== Software Design =====
Line 76: Line 102:
   * **Laboratory 7 (I2C/​TWI):​** Control of the OLED screen via the I2C protocol, using the hardware TWI module of the microcontroller.   * **Laboratory 7 (I2C/​TWI):​** Control of the OLED screen via the I2C protocol, using the hardware TWI module of the microcontroller.
 </​note>​ </​note>​
 +
 +===== Software Design =====
 +
 +==== Overview ====
 +
 +The firmware is written entirely in bare-metal C with no external Arduino libraries.
 +All peripheral control is done through direct manipulation of ATmega328P hardware
 +registers (TWBR, TWCR, TWDR, DDRD, PORTD, PIND). The project is built with
 +PlatformIO using the AVR-GCC toolchain.
 +
 +**Key metrics:**
 +  * ~300 lines of non-trivial,​ original code (''​src/​main.cpp''​)
 +  * RAM usage: 54.8% (1123 / 2048 bytes)
 +  * Flash usage: 11.4% (3502 / 30720 bytes)
 +
 +==== Laboratory Concepts Applied ====
 +
 +<note tip>
 +  * **Laboratory 0 (GPIO):** Buttons on PD4/PD5/PD6 configured as digital inputs with internal pull-ups via DDRD/PORTD registers. State is polled each loop iteration directly from the PIND register. DFPlayer communication pins (PD2, PD3) configured as output/​input via the SOFT_DDR register.
 +  * **Laboratory 1 (UART):** Software UART (bit-banging at 9600 baud, 104µs bit period) implemented from scratch for bidirectional communication with the DFPlayer Mini. TX sends 10-byte command packets; RX reads 10-byte response packets with a timeout. No hardware USART registers are used — the serial protocol is reproduced entirely in software.
 +  * **Laboratory 6 (I2C/​TWI):​** Hardware TWI module used to drive the SSD1306 OLED display. Direct register access: TWBR=72 (100kHz clock), TWCR for START/​STOP/​ACK control, TWDR for data bytes. A full 1024-byte framebuffer is flushed to the display every frame via 32-byte I2C transactions.
 +</​note>​
 +
 +==== Pin Mapping (Firmware) ====
 +
 +^ Component ​        ^ Arduino Pin        ^ Register ​     ^ Type                       ^
 +| OLED Display ​     | A4 (SDA), A5 (SCL) | TWBR,​TWCR,​TWDR | I2C Hardware TWI          |
 +| DFPlayer RX       | D2 (PD2)           | PORTD bit 2   | Software UART TX (bit-bang) |
 +| DFPlayer TX       | D3 (PD3)           | PIND bit 3    | Software UART RX (bit-bang) |
 +| Button PREV/​VOL- ​ | D4 (PD4)           | PIND bit 4    | Digital Input, pull-up ​    |
 +| Button PLAY/PAUSE | D5 (PD5)           | PIND bit 5    | Digital Input, pull-up ​    |
 +| Button NEXT/​VOL+ ​ | D6 (PD6)           | PIND bit 6    | Digital Input, pull-up ​    |
 +| Audio Jack        | DAC_R, DAC_L       | —             | Analog (DFPlayer direct) ​  |
 +
 +==== State Machine ====
 +
 +The main loop (~110ms/​iteration) drives a three-state UI machine:
 +
 +^ State        ^ ui_mode ^ Enter trigger ​                    ^ Exit trigger ​                 ^
 +| Track Mode   | 0       | Default / PLAY in vol / timeout ​  | —                             |
 +| Volume Mode  | 1       | Long-press PREV or NEXT (>​880ms) ​ | Short PLAY or 5s inactivity ​  |
 +| Track Select | 2       | Long-press PLAY                   | Short/long PLAY confirms track |
 +
 +In each state the OLED shows different content. Transitions are triggered exclusively
 +by button events or a countdown timer (vol_timer),​ with no hardware interrupts —
 +the entire logic is polling-based inside the main while(1) loop.
 +
 +==== Software UART — DFPlayer Communication ====
 +
 +The DFPlayer Mini communicates at 9600 baud using 10-byte packets:
 +
 +  0x7E  0xFF  0x06  CMD  0x00  P1  P2  CHK_H  CHK_L  0xEF
 +
 +A bit-bang TX function drives the PD2 line manually, respecting the 104µs bit period:
 +
 +<code c>
 +static void suart_tx_byte(uint8_t b) {
 +    SOFT_PORT &= ~(1 << SOFT_TX_PIN); ​ // start bit
 +    _delay_us(104);​
 +    for (uint8_t i = 0; i < 8; i++) {
 +        if (b & 0x01) SOFT_PORT |=  (1 << SOFT_TX_PIN);​
 +        else          SOFT_PORT &= ~(1 << SOFT_TX_PIN);​
 +        _delay_us(104);​
 +        b >>= 1;
 +    }
 +    SOFT_PORT |= (1 << SOFT_TX_PIN); ​  // stop bit
 +    _delay_us(104);​
 +}
 +</​code>​
 +
 +A matching RX function reads PD3 with a timeout, used at startup to query the
 +total number of tracks on the SD card (command 0x48). If the DFPlayer does not
 +respond, the firmware falls back to a compile-time constant (MAX_TRACKS_FALLBACK = 6).
 +
 +Commands used at runtime:
 +
 +^ Command ^ Hex  ^ Description ​                  ^
 +| Reset   | 0x0C | Hardware reset on startup ​     |
 +| Volume ​ | 0x06 | Set volume level (0–30) ​       |
 +| Play    | 0x03 | Play specific track by number ​ |
 +| Pause   | 0x0E | Pause playback ​                |
 +| Resume ​ | 0x0D | Resume playback ​               |
 +| Query   | 0x48 | Read total file count from SD  |
 +
 +==== I2C / OLED Display System ====
 +
 +A 1024-byte framebuffer (''​oled_buf[1024]''​) mirrors the 128×64 display in RAM
 +(8 pages × 128 columns, 1 bit per pixel). Each frame follows this sequence:
 +
 +  - ''​memset(oled_buf,​ 0, 1024)''​ — clear the buffer
 +  - ''​draw_ui()''​ — render current UI state into the buffer
 +  - ''​oled_flush()''​ — push the full buffer to the OLED via I2C
 +
 +Two rendering primitives exist with an important constraint:
 +
 +  * ''​oled_char(col,​ page, c)''​ — writes a 5×8 font glyph using byte assignment (''​=''​)
 +  * ''​draw_pixel(x,​ y, color)''​ — sets/clears one pixel using bitwise OR (''​ |= ''​)
 +
 +<note warning>
 +''​oled_char''​ uses ''​=''​ which overwrites the entire page byte, erasing any pixels
 +set by ''​draw_pixel''​ in the same column. The screen layout is designed so that
 +each page is used exclusively by text OR by pixels, never both simultaneously.
 +</​note>​
 +
 +The font is a 42-entry 5×8 bitmap array stored in PROGMEM (Flash), covering
 +digits 0–9, letters A–Z, and special glyphs for '':'',​ ''​-'',​ ''​%'',​ ▶ (\\x01), ⏸ (\\x02).
 +
 +==== Big Character Rendering ====
 +
 +To make the track number and volume level visually prominent, a ''​draw_big_char()''​
 +function scales every 5×8 glyph to 10×16 pixels by replacing each source pixel
 +with a 2×2 block:
 +
 +<code c>
 +static void draw_big_char(uint8_t x, uint8_t y_top, char c) {
 +    // resolve font index for c ...
 +    for (uint8_t col = 0; col < 5; col++) {
 +        uint8_t bits = pgm_read_byte(&​font5x8[idx][col]);​
 +        for (uint8_t row = 0; row < 8; row++) {
 +            if (bits & (1 << row)) {
 +                draw_pixel(x + col*2, ​    y_top + row*2, ​    1);
 +                draw_pixel(x + col*2 + 1, y_top + row*2, ​    1);
 +                draw_pixel(x + col*2, ​    y_top + row*2 + 1, 1);
 +                draw_pixel(x + col*2 + 1, y_top + row*2 + 1, 1);
 +            }
 +        }
 +    }
 +}
 +</​code>​
 +
 +In track mode this renders ''​⏸ 01''​ (icon + two digits) centered in the top 16px.
 +In volume mode it renders the current level ''​05''​ centered in the same area.
 +
 +==== Spectrum Bar Animation ====
 +
 +15 bars are animated each frame using a smooth target-following algorithm:
 +
 +  * 20% chance per frame: pick a new random target height for that bar (3–28px)
 +  * Attack: bar grows +2px/frame toward its target
 +  * Decay: bar shrinks −1px/​frame toward its target
 +  * On pause: all bars and targets are instantly set to 0 → flat baseline line
 +
 +This produces a natural-looking equalizer effect without any real audio analysis.
 +
 +==== Button Logic ====
 +
 +Each button uses a hold counter incremented every ~110ms loop iteration:
 +
 +  * **Short press** (released with counter 1–8): change track / adjust volume / play-pause
 +  * **Long press** (counter exceeds 8, ~880ms): open volume menu (PREV/NEXT) or track select menu (PLAY)
 +
 +A ''​prev_long''​ / ''​next_long''​ flag is set on long-press activation to prevent
 +the short-press action from also firing when the button is released.
 +
 +Volume is stored as an integer 0–10. The DFPlayer always receives ''​vol_level × 3''​
 +(mapping 0–10 → 0–30, which is the DFPlayer'​s native 0–30 range).
 +
 +==== Screen Layout ====
 +
 +<​code>​
 +Track Mode:
 +  y=0–15 ​ (pages 0–1): ​ ⏸/​▶ ​ 01    ← 16px big characters (draw_pixel only)
 +  y=16:                 ​──────── ​    ← separator line
 +  y=17–47 (pages 2–5): ​ spectrum ​   ← 15 bars × 5px wide, 7px spacing
 +  y=47:                 ​──────── ​    ← baseline
 +  y=48–55 (page 6):     ​00:​23 ​      ← elapsed time (oled_char only)
 +  y=62–63 (page 7):     ​▓▓▓░░░ ​     ← 2px volume fill bar (draw_pixel only)
 +
 +Volume Mode:
 +  y=0–15 ​ (pages 0–1): ​ 05          ← big vol number (draw_pixel only)
 +  y=16–39 (pages 2–4): ​ [▓▓▓░░░] ​  ← horizontal fill bar with border
 +  y=40–47 (page 5):     ​VOLUME ​     ← label (oled_char only)
 +  y=62–63 (page 7):     ​▓▓▓░░░ ​     ← same 2px volume indicator
 +
 +Track Select:
 +  page 0:    SELECT TRACK
 +  pages 1–6: ▶ 01 / 02 / ... / 06  ← cursor + track number list
 +  page 7:    PLAY=CONFIRM
 +</​code>​
  
 ===== Results ===== ===== Results =====
 +
 +The final product is a functional, standalone portable MP3 player built around
 +the Arduino Nano and DFPlayer Mini, with a custom OLED user interface written
 +entirely without external libraries.
 +
 +{{:​pm:​prj2026:​theodor_ioan.buliga:​whatsapp_image_2026-05-27_at_08.06.10.jpeg?​500|}}
 +{{:​pm:​prj2026:​theodor_ioan.buliga:​whatsapp_image_2026-05-27_at_08.08.02.jpeg?​500|}}
 +{{:​pm:​prj2026:​theodor_ioan.buliga:​whatsapp_image_2026-05-27_at_08.05.58.jpeg?​500|}}
 +**Functional features achieved:**
 +  * Playback of MP3 files from a microSD card with Play / Pause / Next / Previous control
 +  * 3-mode user interface (Track Mode, Volume Mode, Track Select) managed by a state machine
 +  * OLED display showing a 16px scaled track number and play/pause icon, a spectrum bar animation, elapsed time, and a volume indicator bar
 +  * Volume control (0–10 steps) via long-press + short-press button interaction
 +  * Track select menu listing all available tracks (auto-detected from SD card at startup)
 +  * Spectrum animation that instantly flattens to a straight line on pause
 +  * Portable power supply: 3.7V Li-Po battery with TP4056 charging and protection circuit
 +  * Stereo audio output via 3.5mm jack
 +
 +**Performance measurements:​**
 +  * Main loop iteration: ~110ms (10ms active delay + ~100ms I2C framebuffer flush)
 +  * Firmware RAM usage: 54.8% (1123 / 2048 bytes) — stable, no stack overflow observed
 +  * Firmware Flash usage: 11.4% (3502 / 30720 bytes) — significant headroom remaining
 +  * Button response latency: max 110ms (one loop iteration) — imperceptible in practice
 +  * Volume range: 0–30 on DFPlayer (mapped from 0–10 user-facing steps)
 +
 +**Hypothesis validation:​** \\
 +The initial hypothesis was confirmed. Offloading audio decoding to the DFPlayer Mini
 +allowed the ATmega328P to dedicate its full cycle budget to UI rendering and button
 +polling, resulting in a smooth 9 FPS display refresh with no audio glitches.
  
 ===== Conclusions ===== ===== Conclusions =====
  
-===== Download =====+The project successfully demonstrates a balanced embedded system where dedicated 
 +hardware (DFPlayer) handles the computationally expensive task (MP3 decoding) while 
 +the microcontroller focuses on user interaction and visual feedback. 
 + 
 +**What worked well:** 
 +  * The bare-metal approach (no libraries) gave full control over timing and memory, 
 +    which was essential given the 2KB RAM constraint of the ATmega328P. 
 +  * The software UART bit-bang implementation proved reliable for DFPlayer communication 
 +    and also enabled reading the track count response at startup. 
 +  * The framebuffer rendering approach (draw everything into RAM, flush once) eliminated 
 +    partial-update artifacts on the OLED. 
 +  * The hold-counter button logic (short press vs. long press on the same button) reduced 
 +    the required number of physical buttons from 5 to 3 without sacrificing usability. 
 + 
 +**Challenges encountered:​** 
 +  * The ''​oled_char''​ (uses ''​=''​) vs. ''​draw_pixel''​ (uses ''​|=''​) page conflict required 
 +    careful layout planning to avoid text erasing pixel graphics in the same page byte. 
 +  * DFPlayer communication is sensitive to timing — the soft UART bit period (104µs) 
 +    had to be exact, and any I2C activity during transmission caused corruption. 
 +  * Power stability: the Li-Po voltage drop under load initially caused resets; 
 +    resolved by adding a decoupling capacitor near the Arduino VIN pin. 
 + 
 +**Future improvements:​** 
 +  * Replace the simulated spectrum with a real VU meter using the ADC and a signal 
 +    tap from the audio line. 
 +  * Add a progress bar using DFPlayer command 0x28 (query current playback position). 
 +  * Design a custom PCB to reduce size and improve reliability over the prototype board.
  
 ===== Journal ===== ===== Journal =====
  
-  * **May 3**: Created the Wiki documentation page and the Block Diagram.+  * **May 3**: Created the Wiki documentation page and the Block Diagram. ​Defined project scope and selected components. 
 +  * **May 10**: Hardware milestone: developed the full system schematic in KiCad including power management (TP4056) and audio routing. Verified I2C and UART pin mapping on breadboard. Confirmed stable communication between Arduino, OLED, and DFPlayer. 
 +  * **May 15**: Implemented bare-metal I2C driver (TWI registers) and basic OLED framebuffer rendering. First text displayed on screen. 
 +  * **May 18**: Implemented software UART TX for DFPlayer control. First successful track playback triggered from firmware. Volume and play/pause commands verified. 
 +  * **May 20**: Added software UART RX and ''​dfp_query_tracks()''​ to auto-detect SD card track count at startup. Verified fallback behavior when DFPlayer does not respond. 
 +  * **May 22**: Implemented spectrum bar animation with smooth attack/​decay algorithm. Added instant flatten-to-line behavior on pause. 
 +  * **May 24**: Designed and implemented the 3-mode state machine (Track / Volume / Track Select). Rewrote button logic to support short press vs. long press on the same button using hold counters. 
 +  * **May 26**: Added ''​draw_big_char()''​ function for 2× scaled 16px characters. Redesigned the OLED layout: big icon + track number in top 16px, spectrum in the middle, time and volume bar at the bottom. 
 +  * **May 27**: Final firmware build verified: RAM 54.8%, Flash 11.4%, no compilation errors. Assembled final prototype on PCB board. Completed Wiki documentation. 
 + 
 ===== Bibliography/​Resources ===== ===== Bibliography/​Resources =====
   * [[https://​ww1.microchip.com/​downloads/​en/​DeviceDoc/​Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf|ATmega328P Datasheet]]   * [[https://​ww1.microchip.com/​downloads/​en/​DeviceDoc/​Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf|ATmega328P Datasheet]]
Line 91: Line 361:
   * PM Laboratories (UART, I2C, Timers)   * PM Laboratories (UART, I2C, Timers)
   * SSD1306 OLED Tutorials   * SSD1306 OLED Tutorials
 +  * Project Schematic: {{:​pm:​prj2026:​theodor_ioan.buliga:​proiect_pm_v2.pdf|}}
 +  * Project Code : {{:​pm:​prj2026:​theodor_ioan.buliga:​portable_mp3_player.zip|}}
  
 <​html><​a class="​media mediafile mf_pdf"​ href="?​do=export_pdf">​Export to PDF</​a></​html>​ <​html><​a class="​media mediafile mf_pdf"​ href="?​do=export_pdf">​Export to PDF</​a></​html>​
pm/prj2026/theodor_ioan.buliga/adrian.vancea.1777838961.txt.gz · Last modified: 2026/05/03 23:09 by adrian.vancea
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