Olimex ESP32-ADF und ESP-IDF / ESP-ADF

Also, damit das hier mal am ordentlichen Platz ist.

Zusammenfassung

Hintergrund:
Ich habe mir (mit guter Absicht!) bei Olimex ein ESP32-ADF besorgt als Basis. Das bringt gute Vorraussetzungen mit für ESPuino, so dachte ich zumindest.
Die Hardware scheint gut, jedoch hapert es mit der Software bzw. den Treibern.

Eigentlich will ich keinen Fork oder sonstwas von ESPuino machen, sondern nur eine Box die mit den RFID-Tags funktioniert. Der Code ist aktuell auch in keinem Zustand, den ich herzeigen kann. Dazu ist vieles noch zu buggy.

PlatformIO fällt als Basis aus, weil es für den ES8388 (Audio DAC+AMP) keinen passenden Treiber gibt. Daher bleibt nur die ESP-IDF bzw. ESP-ADF nativ. Diese bietet viele Vorteile, jedoch muss die Software neu geschrieben werden.

Hardware im Detail:
Das Olimex ESP32-ADF besteht aus

  • ESP32-WROVER
  • ES8388
  • IR-Receiver
  • 3.5mm Klinkenstecker für Kopfhörer
  • 2x 3W Lautsprecher
  • Einige Buttons (4x touch, 3x tactile)
  • 2 Mikrophone
  • LiPo Ladeelektronik

Ich verwende dazu noch einen RC522 via SPI, sowie einen MCP23017 I2C-Port-Expander.
Die Bedienung mit 3 Buttons und einem Rotary-Encoder gefällt mir gut, habe ich vorerst übernommen.

Es gibt zwar einige Beispiele mit dem ESP-ADF um MP3s zu Streamen bzw. von der SD-Karte abzuspielen, jedoch ist es nicht ganz so trivial, alles zugleich zum Laufen zu bringen. Zudem gibts mit jedem Update der ESP-IDF bzw. ESP-ADF immer wieder so Probleme.
SPI und die SD-Karte und RC522 zugleich haben mir einiges an Kopfzerbrechen bereitet. Mit einem Update der ESP-IDF waren die Probleme dann mit einem Schlag gelöst!

tl;dr

ESP-IDF v4.2.1
ESP-ADF v2.3

Aktueller Stand:

  • MP3-Wiedergabe funktioniert von SD-Karte
  • RC522 funktioniert
  • MCP23017 Port Expander funktioniert
  • Kopfhörerverstärker und Kopfhörererkennung funktionieren
  • Deep-Sleep und Aufwachen funktioniert
  • Buttons und Drehencoder über MCP23017 funktionieren
  • SPI und I2C nutzt Hardware
  • Interrupts für MCP23017

ToDo:

  • WiFi
  • Web Interface
  • Andere Codecs als MP3 sauber unterstützen
  • NeoPixel oder andere LEDs

Olimex stellt ordentliche Boards her. Ich hatte selbst welche, allerdings noch mit PIC Mikrocontroller. Der Unterschied zu den gängigen Lösungen hier im Forum liegt am verwendeten DAC. Olimex benutzt den ES8388. De WS8388 wird ähnlich dem A1S über I2C konfiguriert. Vielleicht hilft der Link zur ES8388 Lib weiter es8388/src at main · maditnerd/es8388 · GitHub

Es ist einfacher den DAC zu integrieren, als die ESPuino Software auf die ESP-IDF Umgebung zu portieren. Das funktioniert auch, macht aber jede Menge Arbeit.

Ja, zudem habe ich auch gelesen dass es einige Probleme mit ESP-IDF und ESP-ADF und PlatformIO gibt.
Inzwischen finde ich aber gefallen an der ESP-IDF / ESP-ADF bzw. FreeRTOS. Man ist näher an der Hardware, mit allen Vor- und Nachteilen.

Das Board funktioniert gut, auch mit den Beispielen kann man sauber mp3s etc streamen. Den Stromverbrauch im Deep-Sleep habe ich noch nicht gemessen, da mache ich mir aber derzeit keine sehr grossen Hoffnungen, dass der extrem niedrig ist.

Um mit dem ES8388 zu experimentieren habe ich das OLIMEX-ADF Bord bestellt.
Wichtig ist die Rev C, weil nur dort ein SD Adapter verbaut ist. Zusätzlich gibt es eine Ladeschaltung für Li-Po Akkus einen IR Empfänger, zwei Tasten, vier Berührungsschaltflächen und zwei Mikrofone. Der I2C Bus des ES8388 ist von außen züganglich, das erspart eine TwoWire Lösung.
Die Dokumentation von OLIMEX könnte besser sein, die Pins für die SD Karte habe ich nur
durch „ausklingeln“ herausgefunden.
Die bereits erwähnte es8388 Lib vonn martinerd funktioniert prima, weitereAnpassungen sind nicht nötig. Der MCLK Pin des ES8388 wird benötigt, sonst kommt die PLL manchmal nicht ganz klar, ich habe i2s_mclk_pin_select(I2S_MCLK);. setzen müssen.
Der ESPuino spielt mit dem ADF Board von OLIMEX. Ich denke, damit funktionieren dann auch die Lyra Boards von Espressif.
Ein kleines Beispiel habe ich auf Github hochgeladen ESP32-audioI2S/ESP32_ES8388.ino at 9783563cb130d6eb6a5074bb000b3b73abe8d2ff · schreibfaul1/ESP32-audioI2S · GitHub

vG Wolle

2 „Gefällt mir“

Genial - Danke!
Werde ich heute gleich Mal testen.

Die Doku von Olimex ist schon ok, suf github gibt es die schematics auch für rev. C. Aber nicht als pdf.

Ich verwende I2C für den port expander, ein uart pin für die Interrupts, und spi für rc522. Alles fein im uext Konnektor. :slight_smile:

Nachdem ich sowas wie „desperate try“ gelesen hatte im Repo dieser Lib, bin ich eigentlich davon ausgegangen, dass es Probleme gibt. Aber das klingt ja gut :slight_smile:

Ok, ESPuino compiliert bei mir grundsätzlich mit PlatformIO in VSCode.
Jetzt müsste ich also eine settings-custom.h anpassen, um das alles zu testen, korrekt?

Wolle, du hast nicht zufällig die nötigen Einstellungen schon parat, oder? :wink:
Wenn das grundsätzlich spielt und tut, würde ich noch meinen Portexpander tauschen, oder prüfen ob man den MCP23017 als option in ESPuino eingebaut kriegt.
Grundsätzlich habt ihr ja mit C++ Code ganz andere Möglichkeiten als die ESP-IDF… :smiley:

kein Problem, hier meine Anpassungen: ESPuino-ES8388.zip.txt (185,1 KB)
Die Konfiguration ist der vom A1S sehr ähnlich, daher habe die settings-a1s.h als Vorlage benutzt und als neuen HAL eingebunden und überall die „twoWires“ in „wire“ umkonfiguriert. Hoffe das hilft.

1 „Gefällt mir“

Also könnte man sicher mal drüber reden, dass man das offiziell in meinen Master-Branch aufnimmt.
Wie flasht man das Board? Ist da ein Serial/USB-Converter drauf oder brauchts nen externen Adapter?

Serial-USB-Converter ist drauf. Es ist ein CH340 verbaut.

Was ich noch nicht genau weiss, ist ob auch über USB der LiPo geladen werden kann, oder nur über den eigene 5V Buchse. Laut Schematic sollte es aber so sein. Ich habs bisher noch nicht ausprobiert.

Wunderbar - Danke! Falls du es noch nicht wusstest, Olimex hat hier alle Schematics, gerber files etc. für das Board veröffentlicht: ESP32-ADF/HARDWARE/ESP32-ADF Rev.C at master · OLIMEX/ESP32-ADF · GitHub
Da kann man auch ganz einfach die Pin-Belegung ablesen.

RC522 ist via SPI nicht supported? Gibts da Probleme mit PIO oder hat das einen anderen Grund?
Mit dem Board und SPI in Hardware läuft das sauber. uSD und RC522 am selben SPI Bus.

Ich hab mal deine Anpassungen eingepflegt, es kompiliert! Für RC522 via SPI waren nur kleine Anpassungen notwendig.

Edit: Grade kurz getestet, also fährt hoch und WLAN etc. tut.
RC522 über SPI funzt auch, die SD-Karte wirft zwar keinen Fehler, aber ich sehe nur „Type: SDHC“ im Log, sonst nix. Die war FAT32 formattiert… sollte doch kein Problem sein, oder?

Edit2: Ich sehe aber dass ESPuino den Ordner __playlist angelegt hat. also klappt das mit der Karte würde ich sagen. Nur wieso sehe ich keine Dateien?

edit3: Grade nochmal auf der selben Hardware meinen Code mit ESP-IDF draufgeladen, die Karte tut. Das meldet mein Code:
Name: SD32G
Type: SDHC/SDXC
Speed: 20 MHz
Size: 29832MB

Und keine Sorge, ich habs 3-5x probiert mit Kaltstart, Karte raus, Karte rein mit ESPuino. Ich denke da gibts Probleme mit dem SPI bus, oder?

Die SD-Karte wir richtig eingebunden sein. Vielleicht wird sie vom RC522 nach der Initialisierung beeinflusst.
In der SW des MFRC522 gibt es eine Reset Routine mittels eines GPIO des ESP32. Das habe ich mir erspart und Rst mit EN des ESP32 verbunden. Frei bleiben darf der Reset Pin des MRFC522 nicht.

Hmm, also ich hatte den nie angeschlossen. Jedoch habe ich RC522 und SD nie so richtig zum Laufen gebracht am gleichen Bus.

Mir war tatsächlich das bis vorgestern auch nicht klar, dass das alles veröffentlicht ist. Also Schaltplan schon, aber dass sogar die KiCad-Files da sind, ist echt mal ne feine Sache.

Hab eben nochmal nachgesehen Using 2 SPI ports on ESP32 ? · Issue #1219 · espressif/arduino-esp32 · GitHub ,das ist richtig, der MFRC522 mag kein weiteres Gerät auf dem SPI, somit muss halt eine zweite SPI Instanz geöffnet werden

Korrektur:
ich habe hier geschrieben, ohne zu testen. Man kann denselben SPI-Bus für RFC und SD zu benutzen. Nur, die RFC Lib kennt HSPI nicht. Standardmäßig läuft aber die SD über HSPI.

auskommentieren: SPIClass spiSD(HSPI);
ändern in SdCard.cpp:
SPI.begin(SPISD_SCK, SPISD_MISO, SPISD_MOSI);
while (!SD.begin(SPISD_CS)) {

und in RfidMrfc522.cpp auskommentieren:
SPI.begin(RFID_SCK, RFID_MISO, RFID_MOSI, RFID_CS); SPI.setFrequency(1000000);

das bleibt so:
static MFRC522 mfrc522(RFID_CS, RST_PIN);

dann funzt es

Naja, mit esp-idf funzt es ja. Auf der selben Hardware…

Edit: Danke Wolle, das liest sich vernünftig. Mir ist schon klar dass mit PlatformIO SPI ja zum grossteil über Software läuft. Ist sicher auch schneller, aber hat halt so seine Eigenheiten.
Ich werde deine Anpassungen mal testen.
Den Reset-Pin habe ich auch nicht angeschlossen, der ist auf Port 99 konfiguriert.

Edit2: Wunderbar, scheint zu laufen. Mit der ESP-IDF habe ich VSPI verwendet, habe gerade nochaml geprüft. Mit HSPI und den Anpassungen von Wolle läuft es auch. Es spielt!

Zum Thema Port-Expander: Kann es sein das biologist direkt ohne Lib den PCA9555 unterstützt? Ich habe ja einen MCP23017 verbaut, der wird ebenfalls über I2C angesprochen und hat ebenfalls 19 Pins. Den gab es fein im DIP-Gehäuse, was sehr bread-board freundlich ist. Der MCP bietet etwas mehr Möglichkeiten als der PCA, das sollte für dieses Projekt aber egal sein.

Ich definiere mal einen PORT_EXPANDER_MCP_ENABLE oder sowas zum testen. Eventuell können dann teile zusammengeführt werden. Das Handling via INT zB…

Edit3: Ich sehe gerade der Rotary geht bei dir nicht über den Port Expander, oder? Also der Button schon, aber die Rotary pins… selbst? Gesehen habe ich dazu jedenfalls nichts.
Ich musste das mangels IOs so umsetzen, das schöne ist dass man hier nix pollen muss, sondern sich auf den INT vom Port Expander verlassen kann.

Edit4: Ok, der PCA9555 kommt wohl recht einfach ohne Library aus. Beim MCP23017 ist es ähnlich, ich versuch das mal so umzusetzen. Der INT Handler Code ist relativ grausam, wie es sauberer gehen kann sieht man bei Button.cpp (Semaphore!) Der Handler auch nicht in den IRAM. Das wäre dann klassisches 1st level, 2nd level interrupt handling. Wenn man das ganze ohne INT macht, kann man die Semaphore gut und gerne von nem Timer-Task anstossen, sollte mit dem gleichen code tun, wenn ich so kurz drüber nachdenke.

Eigentlich bin ich ein großer Freund davon, es mit einem Timer zu machen. Das siehst ja auch am Rest vom Code. Ganz einfach deswegen, weil man dann definierte Verhältnisse hat. Es hat sich allerdings gezeigt, dass die Audioausgabe schlecht klingt, wenn der Expander-Kram ständig mit einem Timer durchlaufen wird. Also irgendwie hat es dann doch zu lange gedauert. Ich habe es dann auf INT umgestellt und war das Problem los.

Ja, das ist so. Ich hab mir das Datenblatt hergenommen und dann halt einfach i2c damit gesprochen. Mehr war es am Ende nicht.

Nein, bisher nicht. Also ich sag mal dagegen sprechen würde nix. Ich war bisher nur zu faul, die Lib anzupassen für den Drehencoder. Das muss ja halt auch alles wieder getestet werden…

Also, der MCP23017 funzt, den Code konnte ich recht einfach mittels einer weiteren #define anpassen. Der Code ist nicht extrem grausam, finde ich. :wink: Outputs über den Port Expander habe ich nicht in Verwendung, kann das daher nicht testen. (Ich hab zwar wo ein paar Neopixel rumliegen, aber bisher einfach noch keine Zeit gehabt.)
Aja: I2C läuft auf diesem Board in einer Instanz gemeinsam mit dem DAC bzw. ES8388. Ich habe daher einfach in der main „i2cBusTwo = Wire;“ gesetzt - nicht sehr elegant, aber es funzt. :wink: :man_shrugging:

WAKEUP_BUTTON habe ich auf PE_INTERRUPT_PIN gesetzt. Damit kann man mit beliebigen Tastendruck über den Port Expander aufwachen. Das könnte man noch explizig konfigurieren, wenn man vor dem Aufwachen die Interrupts nur für bestimmte Ports aktiviert. Vorerst für mich nicht nötig.

Für den Rotary Encoder sieht es auf den ersten Blick auch ganz gut aus. Ich kann natürlich die Hardware des ESP32 für die Pulse Counter nicht verwenden, sondern muss in Software die Rotary Signale decoden und auch entprellen. Ich habe zum Test mal die InterruptEncoder klasse die in ESP32Encoder enthalten ist getestet. Fazit: Hohe Fehlerquote, aber der Ansatz kann funktionieren. Ich werde die Lib die ich mit ESP-IDF verwendet habe, nachbauen. Da gibt es eine State Machine, damit funzt mein Encoder sauber, auch ohne extra Hardware.

Ansonsten scheint Audio Ausgabe etc. zu funzen. Die Lautstärke ist auch mit 1 noch relativ hoch, finde ich. Es ist 2x ein unangenehmes Rauschen beim Booten zu vernehmen. Ich bin noch nicht ganz dahinter gekommen, wie man das lösen kann.

Also falls Neopixel für den Port Expander gemeint war: Das kannst du vergessen, das ist viel zu zeitkritisch.

Drehencoder geht aber auf jeden Fall. Vielleicht setze ich mich ja mal irgendwann dran.

Also die Lib für den Rotary die ich probiert habe basiert auf der hier: arduino/libraries/Rotary at master · buxtronix/arduino · GitHub
Die funktioniert fein wenn man die direkt über den Interrupt vom Port Expander ansteuert. Ich hab jetzt mal versucht die direkt in RotaryEncoder_Cyclic() aufzufuren, aber das funktioniert kaum. Ich versuche es mal beim Interrupt-Handler aufzurufen…
Schön wäre es, weil hier könnte man einfach statt digitalRead() Port_Read() verwenden und die Sache hätte sich. Typischer Fall von Denkste! :frowning: :wink:

Ausserdem funktioniert der RC522 nach dem Tiefschlaf nicht mehr. Das dürfte wohl mit den Anpassungen am SPI zu tun haben, oder? Ich hab den Code noch nicht geprüft.