This shows you the differences between two versions of the page.
|
pm:prj2026:theodor_ioan.buliga:ioan.sabou [2026/05/18 09:37] ioan.sabou [Introducere] |
pm:prj2026:theodor_ioan.buliga:ioan.sabou [2026/05/27 01:12] (current) ioan.sabou [Scheme electrice] |
||
|---|---|---|---|
| Line 7: | Line 7: | ||
| Ideea principală: o fantomă (Ghost Boo) afișată pe un ecran OLED devine tristă în timp. Pentru a o menține fericită, utilizatorul trebuie să joace minigame-uri — unele dintre ele implică construirea unor circuite electronice reale pe breadboard, pe care ESP32 le validează și le face graficul pe un al doilea ecran OLED dedicat minigame-urilor. | Ideea principală: o fantomă (Ghost Boo) afișată pe un ecran OLED devine tristă în timp. Pentru a o menține fericită, utilizatorul trebuie să joace minigame-uri — unele dintre ele implică construirea unor circuite electronice reale pe breadboard, pe care ESP32 le validează și le face graficul pe un al doilea ecran OLED dedicat minigame-urilor. | ||
| - | Scopul proiectului este de a oferi un sistem de recompensă concret (menținem fantoma fericită) prin construirea și înțelegerea unor circuite electronice, învățând astfel concepte de bază din electronică și fizică: | + | Scopul proiectului este de a oferi un sistem de recompensă concret (menținem fantoma fericită) prin construirea și înțelegerea unor circuite electronice, învățând astfel concepte de bază din electronică și fizică. |
| - | - Voltage divider | + | Proiectul este finalizat cu 9 minigame-uri funcționale: potentiometer, RC circuit (6 configurații), voltage divider, diode rectifier, transistor switch, NPN amplifier, deadbeet rhythm, și metronome. |
| - | - RC circuit — utilizatorul primește un τ țintă, fantoma face graficul curbei de descărcare pe osciloscop live | + | |
| - | - RC high-pass / low-pass filter — fantoma devine fericită dacă filtrul funcționează corect | + | |
| - | - Diode half-wave rectifier | + | |
| - | - Capacitor as power buffer — ESP taie GPIO-ul, ADC-ul măsoară cât timp condensatorul menține tensiunea | + | |
| - | - Transistor switch — ESP conduce baza, măsoară colectorul | + | |
| - | Abstractizarea generală a conceptului de „circuit minigame" ca struct `minigame_t` este **implementată**: fiecare minigame expune `init`, `deinit`, `run` și un `mood_reward`, toate înregistrate într-un registry cu progresie liniară. | + | Abstractizarea generală a conceptului de „circuit minigame" ca struct `minigame_t` este implementată: fiecare minigame expune `init`, `deinit`, `run` și un `mood_reward`, toate înregistrate într-un registry cu progresie liniară. |
| - | + | ||
| - | Am pornit de la ideea de a face un personaj tip Tamagotchi care devine fericit când este încălzit (senzor de temperatură extern) și am ajuns la un sistem complet de minigame-uri de electronică. | + | |
| Proiectul este util pentru că stimulează dorința de a învăța electronică și de a construi circuite corecte, într-un mod interactiv și vizual. | Proiectul este util pentru că stimulează dorința de a învăța electronică și de a construi circuite corecte, într-un mod interactiv și vizual. | ||
| Line 29: | Line 22: | ||
| | 0 | Potentiometer | Wiperul unui potențiometru; menții zona țintă random 3s | GPIO32 (ADC1 CH4) | ✓ Implementat | | | 0 | Potentiometer | Wiperul unui potențiometru; menții zona țintă random 3s | GPIO32 (ADC1 CH4) | ✓ Implementat | | ||
| | 1 | RC circuit (6 config.) | Construiești R+C pe breadboard; ESP măsoară τ prin least-squares pe ln(V) | GPIO25 out, GPIO34 ADC | ✓ Implementat | | | 1 | RC circuit (6 config.) | Construiești R+C pe breadboard; ESP măsoară τ prin least-squares pe ln(V) | GPIO25 out, GPIO34 ADC | ✓ Implementat | | ||
| - | | 2 | Voltage divider | Doi rezistori în serie; ESP validează raportul de tensiune | GPIO25 out, GPIO34 ADC | TODO | | + | | 2 | Voltage divider | Doi rezistori în serie; ESP validează raportul de tensiune | GPIO25 out, GPIO34 ADC | ✓ Implementat | |
| | 3 | RC low/high-pass filter | R+C; ESP măsoară răspunsul în frecvență la PWM | GPIO25 PWM, GPIO34 ADC | TODO | | | 3 | RC low/high-pass filter | R+C; ESP măsoară răspunsul în frecvență la PWM | GPIO25 PWM, GPIO34 ADC | TODO | | ||
| - | | 4 | Diode rectifier | Diodă + condensator pe semnal PWM redresat | GPIO25 PWM, GPIO34 ADC | TODO | | + | | 4 | Diode rectifier | Diodă + condensator pe semnal cosine 200Hz redresat; osciloscop live | GPIO25 DAC, GPIO34 ADC | ✓ Implementat | |
| | 5 | Capacitor buffer | Condensatorul menține tensiunea după tăierea GPIO-ului | GPIO25 out, GPIO34 ADC | TODO | | | 5 | Capacitor buffer | Condensatorul menține tensiunea după tăierea GPIO-ului | GPIO25 out, GPIO34 ADC | TODO | | ||
| - | | 6 | Transistor switch | NPN; ESP conduce baza, măsoară colectorul | GPIO25 out (bază), GPIO34 ADC (colector) | TODO | | + | | 6 | Transistor switch | NPN în saturație (1kΩ bază); ESP conduce baza, măsoară Vc la colector | GPIO25 out (bază), GPIO34 ADC (colector) | ✓ Implementat | |
| + | | 6b | NPN Amplifier | NPN în regim activ (1MΩ bază); ESP măsoară β real (Ib=2.6µA fix) | GPIO25 out (bază), GPIO34 ADC (colector) | ✓ Implementat | | ||
| | 7 | LM-35 temperature | Senzor temperatură extern, output direct pe ADC | GPIO35 (ADC1 CH7) | TODO | | | 7 | LM-35 temperature | Senzor temperatură extern, output direct pe ADC | GPIO35 (ADC1 CH7) | TODO | | ||
| - | | 8 | Deadbeet rhythm | ESP redă o secvență de LED-uri; utilizatorul o repetă pe butoane | GPIO13/12/14 out, GPIO15/4/5 in | TODO | | + | | 8 | Deadbeet rhythm | ESP redă o secvență de LED-uri; utilizatorul o repetă pe butoane colorate | GPIO13/12/14 out (LED), GPIO19/17/16 in (R/G/B) | ✓ Implementat | |
| - | | 9 | Metronome | Două potențiometre (BPM + duty cycle) + buzzer pasiv | GPIO32, GPIO35 ADC; GPIO23 PWM | TODO | | + | | 9 | Metronome | Două potențiometre (BPM + duty cycle) + buzzer pasiv; ține 3s pentru pass | GPIO32, GPIO35 ADC; GPIO23 PWM | ✓ Implementat | |
| - | Games 1–6 partajează GPIO25 și GPIO34 — circuitul de pe breadboard se schimbă între ele, nu pinii. | + | Games 1–6b partajează GPIO25 și GPIO34 — circuitul de pe breadboard se schimbă între ele, nu pinii. |
| ==== Schema bloc ==== | ==== Schema bloc ==== | ||
| Line 50: | Line 44: | ||
| * **OLED SSD1306 0.96"** — display dedicat minigame-urilor: instrucțiuni, bare de progres, osciloscop live | * **OLED SSD1306 0.96"** — display dedicat minigame-urilor: instrucțiuni, bare de progres, osciloscop live | ||
| * **Buzzer pasiv (PWM)** — folosit în metronome minigame | * **Buzzer pasiv (PWM)** — folosit în metronome minigame | ||
| - | * **LED-uri** — folosite în deadbeet rhythm game | + | * **LED-uri RGB** — folosite în deadbeet rhythm game |
| * **Butoane tactile** — input pentru deadbeet rhythm game + butonul principal BTN_A (GPIO33) | * **Butoane tactile** — input pentru deadbeet rhythm game + butonul principal BTN_A (GPIO33) | ||
| - | * **ADC GPIO34** — măsurare circuite RC, voltage divider, filter, etc. | + | * **ADC GPIO34** — măsurare circuite RC, voltage divider, rectifier, transistor, etc. |
| - | * **ADC GPIO35** — senzor temperatură LM-35 | + | * **ADC GPIO35** — senzor temperatură LM-35 / duty cycle metronome |
| * **ADC GPIO32** — potențiometru minigame / BPM metronome | * **ADC GPIO32** — potențiometru minigame / BPM metronome | ||
| Line 63: | Line 57: | ||
| ^ Display ^ Diagonală ^ Rol ^ Bus I2C ^ SDA ^ SCL ^ Adresă ^ | ^ Display ^ Diagonală ^ Rol ^ Bus I2C ^ SDA ^ SCL ^ Adresă ^ | ||
| - | | SH1106 | 1.3" | Ghost — permanent activ | I2C0 (I2C_NUM_0) | GPIO21 | GPIO22 | 0x3C | | + | | SH1106 | 1.3" | Ghost — permanent activ | I2C0 (I2C_NUM_0) | GPIO18 | GPIO5 | 0x3C | |
| - | | SSD1306 | 0.96" | Minigame support | I2C1 (I2C_NUM_1) | GPIO26 | GPIO27 | 0x3C | | + | | SSD1306 | 0.96" | Minigame support | I2C1 (I2C_NUM_1) | GPIO4 | GPIO15 | 0x3C | |
| - | Ambele display-uri au adresa 0x3C — menținute pe bus-uri separate pentru a evita conflictul. | + | Ambele display-uri au adresa 0x3C — menținute pe bus-uri separate pentru a evita conflictul. Ambele sunt cablate pe Side 1 al DevKit-ului pentru cablaj curat. |
| === Pini minigame-uri === | === Pini minigame-uri === | ||
| Line 75: | Line 69: | ||
| | 2 | Voltage divider | Doi rezistori în serie | GPIO25 out, GPIO34 ADC | | | 2 | Voltage divider | Doi rezistori în serie | GPIO25 out, GPIO34 ADC | | ||
| | 3 | RC low/high-pass filter | R+C, măsurare răspuns frecvență | GPIO25 PWM, GPIO34 ADC | | | 3 | RC low/high-pass filter | R+C, măsurare răspuns frecvență | GPIO25 PWM, GPIO34 ADC | | ||
| - | | 4 | Diode rectifier | Diodă + cap pe PWM redresat | GPIO25 PWM, GPIO34 ADC | | + | | 4 | Diode rectifier | Diodă + cap pe semnal cosine redresat | GPIO25 DAC, GPIO34 ADC | |
| | 5 | Capacitor buffer | Cap menține tensiunea după GPIO cut | GPIO25 out, GPIO34 ADC | | | 5 | Capacitor buffer | Cap menține tensiunea după GPIO cut | GPIO25 out, GPIO34 ADC | | ||
| - | | 6 | Transistor switch | NPN bază drive, măsurare colector | GPIO25 out (bază), GPIO34 ADC (colector) | | + | | 6 | Transistor switch | NPN saturație: 1kΩ bază, LED pe colector | GPIO25 out (bază 1kΩ), GPIO34 ADC (colector) | |
| + | | 6b | NPN Amplifier | NPN activ: 1MΩ bază, 1kΩ colector, β real | GPIO25 out (bază 1MΩ), GPIO34 ADC (colector) | | ||
| | 7 | Temperature LM-35 | Output LM-35 la ADC | GPIO35 (ADC1 CH7) | | | 7 | Temperature LM-35 | Output LM-35 la ADC | GPIO35 (ADC1 CH7) | | ||
| - | | 8 | Deadbeet rhythm | LED-uri secvență, butoane repetă | GPIO13/12/14 out (LED), GPIO15/4/5 in (butoane) | | + | | 8 | Deadbeet rhythm | LED-uri secvență, butoane colorate repetă | GPIO13/12/14 out (R/G/B LED), GPIO19/17/16 in (R/G/B buton) | |
| | 9 | Metronome | Două pot-uri + buzzer pasiv | GPIO32 ADC (BPM), GPIO35 ADC (duty), GPIO23 PWM | | | 9 | Metronome | Două pot-uri + buzzer pasiv | GPIO32 ADC (BPM), GPIO35 ADC (duty), GPIO23 PWM | | ||
| Line 92: | Line 87: | ||
| ^ # ^ Componentă ^ Cantitate ^ Observații ^ | ^ # ^ Componentă ^ Cantitate ^ Observații ^ | ||
| | 1 | ESP-32 Devkit V1 (DOIT) | 1 | Microcontroller principal | | | 1 | ESP-32 Devkit V1 (DOIT) | 1 | Microcontroller principal | | ||
| - | | 2 | Ecran OLED SH1106 1.3", 128×64, I2C | 1 | SDA→GPIO21, SCL→GPIO22; display fantomă | | + | | 2 | Ecran OLED SH1106 1.3", 128×64, I2C | 1 | SDA→GPIO18, SCL→GPIO5; display fantomă | |
| - | | 3 | Ecran OLED SSD1306 0.96", 128×64, I2C | 1 | SDA→GPIO26, SCL→GPIO27; display minigame | | + | | 3 | Ecran OLED SSD1306 0.96", 128×64, I2C | 1 | SDA→GPIO4, SCL→GPIO15; display minigame | |
| | 4 | Senzor temperatură LM-35 | 1 | ADC→GPIO35 | | | 4 | Senzor temperatură LM-35 | 1 | ADC→GPIO35 | | ||
| - | | 5 | Butoane tactile | 4 | 3× deadbeet rhythm + 1× BTN_A principal | | + | | 5 | Butoane tactile + set butoane colorate SYB-120 | 4+ | 3× deadbeet rhythm (R/G/B) + 1× BTN_A principal | |
| - | | 6 | LED-uri | 3 | Deadbeet rhythm game | | + | | 6 | LED-uri RGB | 3 | Deadbeet rhythm game (R/G/B) | |
| | 7 | Buzzer pasiv | 1 | Metronome minigame (PWM GPIO23) | | | 7 | Buzzer pasiv | 1 | Metronome minigame (PWM GPIO23) | | ||
| - | | 8 | Potențiometre | 2 | 1× potentiometer minigame (GPIO32), 1× metronome BPM | | + | | 8 | Potențiometre | 2+ | 1× potentiometer minigame (GPIO32), 1× metronome duty (GPIO35); potențiometre colorate SYB-120 | |
| - | | 9 | Rezistențe, condensatoare, diode, tranzistoare | asortate | Pentru circuit minigame-uri (RC, divider, filter, rectifier, switch) | | + | | 9 | Rezistențe, condensatoare, diode | asortate | Pentru circuit minigame-uri (RC, divider, filter, rectifier) | |
| - | | 10 | Fire de breadboard + breadboard | — | Conectică generală | | + | | 10 | Tranzistoare NPN 2N2222 | 2+ | Transistor switch minigame (1kΩ bază) + NPN amplifier (1MΩ bază) | |
| + | | 11 | Kit breadboard SYB-120 (Optimus Digital) | 1 | 4 breadboard-uri, fire, butoane colorate, potențiometre | | ||
| + | | 12 | Fire de breadboard | — | Conectică generală | | ||
| ==== Scheme electrice ==== | ==== Scheme electrice ==== | ||
| - | **RC circuit (GPIO25 → R → nod → C → GND, GPIO34 la nod):** ✓ Realizat | + | {{:pm:prj2026:theodor_Ioan.buliga:schema_rc.pdf|Schema hardware placa (PDF)}} |
| - | {{:pm:prj2026:theodor_Ioan.buliga:schema_rc.pdf|Schema RC circuit (PDF)}} | + | **Voltage divider, RC circuit diode rectifier, transistor switch, NPN amplifier, deadbeet rhythm, metronome:** Vezi caietul cu ecuațiile și schemele pentru referință. |
| - | + | ||
| - | **TODO:** Schemă electrică pentru voltage divider minigame | + | |
| **TODO:** Schemă electrică pentru RC low/high-pass filter | **TODO:** Schemă electrică pentru RC low/high-pass filter | ||
| - | |||
| - | **TODO:** Schemă electrică pentru diode rectifier | ||
| **TODO:** Schemă electrică pentru capacitor buffer | **TODO:** Schemă electrică pentru capacitor buffer | ||
| - | |||
| - | **TODO:** Schemă electrică pentru transistor switch (NPN, ESP conduce baza, măsoară colectorul) | ||
| **TODO:** Schemă electrică pentru LM-35 temperature sensor | **TODO:** Schemă electrică pentru LM-35 temperature sensor | ||
| - | |||
| - | **TODO:** Schemă electrică pentru deadbeet rhythm game (3 butoane + 3 LED-uri) | ||
| - | |||
| - | **TODO:** Schemă electrică pentru metronome minigame (2 potențiometre + buzzer pasiv) | ||
| ==== Diagrame de semnal ==== | ==== Diagrame de semnal ==== | ||
| - | **RC circuit minigame — curba de descărcare:** ✓ Afișată live pe SSD1306 ca osciloscop (V pe axa Y, timp pe axa X). τ măsurat suprapus cu τ țintă pe ecranul de rezultate. | + | Vezi caietul cu ecuațiile și schemele pentru referință. |
| - | + | ||
| - | **TODO:** Diagramă semnal pentru deadbeet rhythm game — secvența ESP vs. secvența introdusă de utilizator | + | |
| - | + | ||
| - | ==== Rezultatele simulării ==== | + | |
| - | + | ||
| - | Validarea se face pe hardware real. Valorile R și C pot fi verificate în prealabil cu Falstad Circuit Simulator. | + | |
| ===== Software Design ===== | ===== Software Design ===== | ||
| Line 185: | Line 166: | ||
| ==== TODO Software ==== | ==== TODO Software ==== | ||
| - | * Voltage divider minigame | ||
| * RC low/high-pass filter minigame | * RC low/high-pass filter minigame | ||
| - | * Diode rectifier minigame | ||
| * Capacitor buffer minigame | * Capacitor buffer minigame | ||
| - | * Transistor switch minigame | ||
| * LM-35 temperature minigame | * LM-35 temperature minigame | ||
| - | * Deadbeet rhythm game | ||
| - | * Metronome minigame | ||
| - | ===== Rezultate Obținute ===== | + | ===== Concluzii ===== |
| - | * Fantomă animată pe SH1106 cu 5 stări și decay de mood funcțional | + | Proiectul a atins obiectivul principal: 9 minigame-uri funcționale, fiecare cerând utilizatorului să construiască un circuit real pe breadboard și să îl valideze cu ESP32. |
| - | * Sistem de abstractizare minigame complet (minigame_t, registry, două task-uri FreeRTOS) | + | |
| - | * Potentiometer minigame funcțional: target random, progress bar, hold 3s | + | |
| - | * RC circuit minigame funcțional: 6 configurații, osciloscop live pe SSD1306, τ măsurat prin least-squares | + | |
| - | ===== Concluzii ===== | + | Abstractizarea `minigame_t` s-a dovedit robustă — fiecare joc nou a necesitat modificări în exact 3 fișiere (minigame nou, `minigame_registry.c`, `minigame_rewards.h`), fără a atinge codul existent. |
| + | |||
| + | Cea mai complexă măsurătoare: RC τ prin least-squares pe ln(V) cu timestamps reale, toleranță ±35% pentru componente reale. Cea mai vizuală: diode rectifier cu osciloscop live pe SSD1306 — 128 sample-uri la 200Hz, 2 cicluri complete pe ecran. | ||
| + | |||
| + | GPIO25 și GPIO34 sunt partajate de 7 jocuri (RC, voltage divider, rectifier, transistor switch, NPN amplifier) — breadboard-ul se schimbă între minigame-uri, pinii nu. Aceasta a simplificat semnificativ cablajul permanent. | ||
| + | |||
| + | Remaparea display-urilor pe Side 1 al DevKit-ului (SSD1306→GPIO4/15, SH1106→GPIO18/5) a eliminat firele lungi care traversau breadboard-ul. | ||
| - | **TODO:** De completat după finalizarea proiectului. | + | Rămân de implementat: RC filter, capacitor buffer și LM-35 — fiecare necesită aproximativ aceeași structură ca jocurile existente. |
| ===== Download ===== | ===== Download ===== | ||
| Line 214: | Line 193: | ||
| * **6 mai** — a doua comandă: ESP-32, SH1106 (OLED 1.3" I2C) | * **6 mai** — a doua comandă: ESP-32, SH1106 (OLED 1.3" I2C) | ||
| * **10 mai** — primul demo funcțional: fantoma animată pe OLED, circuit RC pe breadboard, grafic al curbei de descărcare afișat pe ecran | * **10 mai** — primul demo funcțional: fantoma animată pe OLED, circuit RC pe breadboard, grafic al curbei de descărcare afișat pe ecran | ||
| - | * **16 mai** — implementat sistem complet de abstractizare minigame (minigame_t, registry, două task-uri FreeRTOS); potentiometer minigame funcțional pe SSD1306 | + | * **16 mai** — sistem complet de abstractizare minigame (minigame_t, registry, două task-uri FreeRTOS); potentiometer minigame funcțional; RC circuit minigame: osciloscop live pe SSD1306, τ prin least-squares, 6 configurații modulare |
| - | * **16 mai** — RC circuit minigame: osciloscop live pe SSD1306, τ prin least-squares cu timestamps reale, 6 configurații modulare (τ = 100ms … 4s, toți parametrii derivați din tau_ms) | + | * **18 mai** — deadbeet rhythm minigame implementat: secvență LED R/G/B generată de ESP, utilizatorul repetă pe 3 butoane |
| + | * **20 mai** — achiziție kit SYB-120 (4 breadboard-uri, butoane colorate, potențiometre) de la Optimus Digital | ||
| + | * **23 mai** — voltage divider minigame implementat | ||
| + | * **23 mai** — remapare display-uri pe Side 1: SSD1306→GPIO4/GPIO15, SH1106→GPIO18/GPIO5; butoane deadbeet remapate pe GPIO19/17/16 | ||
| + | * **23 mai** — metronome minigame implementat: două potențiometre (BPM + duty cycle) + buzzer pasiv, hold 3s pentru pass | ||
| + | * **23 mai** — diode rectifier minigame implementat: DAC cosine 200Hz pe GPIO25, osciloscop live SSD1306, validare prin std dev < 100mV | ||
| + | * **24 mai** — NPN transistor switch minigame implementat (1kΩ bază, LED pe colector, pass dacă Vc/Vcc < 0.15); adăugat NPN_THEORY.md cu pinout 2N2222 | ||
| + | * **26 mai** — NPN amplifier variant implementat: 1MΩ bază (Ib=2.6µA), SSD1306 afișează Vc, Ic și β real; pass dacă 20 ≤ β ≤ 800 | ||
| ===== Bibliografie/Resurse ===== | ===== Bibliografie/Resurse ===== | ||
| Line 223: | Line 209: | ||
| * Datasheet SSD1306 — [[https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf]] | * Datasheet SSD1306 — [[https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf]] | ||
| * Datasheet LM-35 — [[https://www.ti.com/lit/ds/symlink/lm35.pdf]] | * Datasheet LM-35 — [[https://www.ti.com/lit/ds/symlink/lm35.pdf]] | ||
| + | * Datasheet 2N2222 — [[https://www.onsemi.com/pdf/datasheet/p2n2222a-d.pdf]] | ||
| * ESP-IDF v5 I2C Master docs — [[https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/i2c.html]] | * ESP-IDF v5 I2C Master docs — [[https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/i2c.html]] | ||
| * ESP-IDF v5 ADC Oneshot docs — [[https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc_oneshot.html]] | * ESP-IDF v5 ADC Oneshot docs — [[https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc_oneshot.html]] | ||
| + | * ESP-IDF v5 DAC Cosine docs — [[https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/dac.html]] | ||
| * ESP-IDF v5 FreeRTOS docs — [[https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos.html]] | * ESP-IDF v5 FreeRTOS docs — [[https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos.html]] | ||
| <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> | ||