forked from hacknology/website
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
8.9 KiB
180 lines
8.9 KiB
---
|
|
title: NodeSP Part I (Analog)
|
|
author: kaqu
|
|
status: Aktiv
|
|
difficulty: geht so ...
|
|
time: ~4h
|
|
date: 2020-02-20
|
|
image: NodeSP_Overview.jpg
|
|
keywords: NodeMCU,ESP32,FFT,Assembler
|
|
---
|
|
|
|
oder
|
|
|
|
ein Beitrag zum Thema *'Wieder versuchen./Wieder scheitern./Besser scheitern'* (Samuel Becket)
|
|
|
|
## Motivation
|
|
|
|
Phail hat mir - temporär - ein ESP32-Dev-Board überlassen. Auf diesem
|
|
ist ein FTDI-Chip zwecks Bereitstellung eines JTAG-Interfaces verbaut.
|
|
Seit Sommer 2019 hat PlatformIO seinen Debugger für die Öffentlichkeit
|
|
freigegeben. Beim Herumspielen in VSC zeigt sich, daß nicht nur
|
|
Source-Level Debugging möglich ist, sondern auch auf die
|
|
(Dis-)Assembler-Ebene gewechselt werden kann.
|
|
Da ließe sich doch vielleicht etwas mit anfangen?
|
|
|
|
{{< fluid_img alt="Showtime" src="NodeSP_Overview.jpg" >}}
|
|
|
|
Wie wäre es mit einer Fingerübung in ESP32-Assembler? Eine FFT hätte
|
|
den Charme einer späteren Nutzbarkeit für verschiedenste Projekte und
|
|
wäre doch ein überschaubarer Aufwand ...
|
|
|
|
## Planung
|
|
|
|
Wichtig ist zunächst einmal, einen brauchbaren Algorithmus zu finden.
|
|
Wenn ein Schneller gefunden ist, könnte dieser als Re-Implementierung
|
|
in ESP32-Assembler vermutlich alle anderen wegblasen?!
|
|
|
|
Anforderungen:
|
|
|
|
- Der verwendete Algorithmus sollte hinreichend allgemein einsetzbar sein,
|
|
d.h. die Meßwertblöcke sollten als 2er-Potenzen formuliert werden
|
|
können.
|
|
|
|
- Außerdem sollte mit echten Fließkommazahlen gearbeitet werden
|
|
(der ESP32 unterstützt 32-Bit IEEE754 mit 16 Registern native).
|
|
|
|
- Für die Meßwerte sollen keine Vorinformationen vorliegen (d.h. beliebige
|
|
Daten sollen verarbeitet werden können).
|
|
|
|
- Neben der Fourier-Transformation soll auch die Inverse berechnet werden
|
|
können.
|
|
|
|
## Ausführung Testprogramm FFT
|
|
|
|
Für die Geschwindigkeitsmessung habe ich zunächst ein Testprogramm in C(99)
|
|
erstellt. Dies soll zunächst verschiedene Algorithmen bzw. deren Implementierungen vergleichen,
|
|
später wird hiermit auch die Effizienz der eigenen Implementierung getestet. Es soll immer der
|
|
gleiche Testmeßdatensatz verwendet werden, für den die korrekten Ergebnisse vorab bekannt sind.
|
|
So wird die Korrektheit der Implementierung verifiziert.
|
|
|
|
Nachdem in meinem Vergleichsfeld ein 'schnellster' Algorithmus ermittelt ist (es liegen mehrere
|
|
10er-Potenzen - in [µs] gemessen - zwischen den verschiedenen Implementierungen), habe ich diesen
|
|
in 'native' ESP32 umgeschrieben (für mathematisch Interessierte: http://www.katjaas.nl/home/home.html ).
|
|
Zunächst gilt es, die Parameterübergabe zu klären, d.h. welche Parameter werden in welchen Registern
|
|
übergeben. Dann muß man die Dokumentation zur Inline-Assembly des GCC zusammensuchen und last (but not
|
|
least) muß natürlich der Assembler-Guide 'Xtensa® Instruction Set Architecture (ISA) Reference Manual'
|
|
durchgearbeitet werden ( https://0x04.net/~mwk/doc/xtensa.pdf ).
|
|
|
|
Im praktischen Teil zeigt sich, daß die aktuelle Kombination VSC/PlatformIO/Xtensa-Debugger
|
|
noch reichlich 'buggy' ist. Beim Single-Stepping durch den Code kommt es immer wieder zum 'Rausfliegen'
|
|
(Rücksprung in die übergeordnete Funktion). Außerdem klappt es mit dem Disassemblieren häufig nicht so
|
|
recht, d.h. Opcodes werden falsch gelesen bzw. an 'falschen' Adressen mit der Disassembly begonnen
|
|
(erkennbar häufig an der komplett sinnfreien Bedeutung des Codes, gelegentlich auch an eingefügten
|
|
.byte-Instruktionen - die hier eigentlich nichts verloren haben ...).
|
|
|
|
Trotz dieser Widrigkeiten habe ich schließlich eine funktionierende Version realisiert.
|
|
|
|
Ergebnis:
|
|
Der Speedup sinkt sehr schnell von 2.2 (für 8 Meßwerte) auf 1.1 (ab 256 Meßwerte & mehr). Mmmh,
|
|
nicht so überzeugend (nur 10%) für den Aufwand (ich hatte mit einem Speedup von 2-5 gerechnet!). Nach einigem
|
|
Kopfkratzen bin ich zu folgenden Erkenntnissen gelangt:
|
|
|
|
- Der limitierende Faktor ist vermutlich der Speicherzugriff. Der ESP32 hat keinen Cache, d.h. alle
|
|
Speicherzugriffe bremsen unmittelbar. Bei größeren Meßwertmengen schlägt das überproportional durch
|
|
(Speicherzugriffe von C<->Assembler sind aufgrund der leistungsfähigen Befehle des 'Instruction Sets'
|
|
gleich schnell, d.h. der Compiler wählt bereits die optimalen Befehle).
|
|
|
|
- Moderne Prozessoren (ESP32 gemäß Doku eine 'post-RISC Architecture') haben sehr leistungsfähige
|
|
Befehle & vergleichsweise viele Register (hier 16 'universelle' in einem Registerfile von 64 und
|
|
16 FP-Register - sowie diverse andere hier nicht so Relevante). Früher war das mal anders ... (6510 et al.)!
|
|
Letztlich kann dadurch der Compiler recht gut optimieren, der Gewinn durch 'Metawissen' seitens
|
|
des Programmierers (also was man weglassen darf u.ä.) ist marginal.
|
|
|
|
- Die speziellen Matrix-Support Operationen (multiply/add w/ storage pointer increment etc.)
|
|
passten leider nicht zu dem von mir favorisierten Algorithmus (dumm gelaufen!).
|
|
|
|
Fazit: Nett, aber (weitgehend) sinnfrei! Eigentlich ist das zwar der Stand der Erkenntnisse (in der
|
|
Informatik), aber schön, das wir das noch einmal überprüft haben ...
|
|
|
|
Jetzt brauchen wir noch etwas 'zum Anfassen' ...
|
|
|
|
## Ausführung 'Spectrum Analyzer' (SP)
|
|
|
|
Naheliegend für einen Realitätscheck ist natürlich der Aufbau eines 'Spectrum Analyzers', so eine
|
|
Art Equalizer wie man es von Audio-Apps, Hifi-Anlagen (oder dem Autoradio) kennt - nur ohne
|
|
Eingriffsmöglichkeit.
|
|
|
|
{{< fluid_img alt="Aufbaubild" src="NodeSP_Aufbau.jpg" >}}
|
|
|
|
Aus alten Beständen hatte ich noch die LED-Streifen (WS2812) aus einem Sonderangebot. Hier fällt etwas
|
|
Lötarbeit an (& die Heißklebepistole kommt auch zu ihrem Recht).
|
|
|
|
{{< fluid_img alt="Bohrschablone" src="NodeSP_Bohrschablone.png" >}}
|
|
|
|
Für das Gehäuse habe ich eine Bohrschablone mit FreeCAD erstellt, die wir auf der neuen 'Großfräse' der
|
|
Innovationswerkstatt dann ausgeführt haben.
|
|
|
|
Neben dem Gehäuse habe ich eine Mikrofonbaugruppe mit eingebauter Verstärkung (40..60dB),
|
|
einen Level-Shifter (3,3<->5V) sowie eine kleine Platine zusätzlich beschafft.
|
|
Referenzen: Adafruit MAX9814-debo-amp-mic2, DEBO LEV SHIFTER
|
|
|
|
Da ich alle meine Projekte bis dato auf NodeMCUs (& esp-idf Framework) realisiert habe, muß jetzt
|
|
ein passendes Modul ('sampling') in 'C' erstellt werden, die Scriptanbindung Richtung Lua gibt es
|
|
gratis dazu (bei sachgerechter Anwendung ... ;-).
|
|
|
|
Im Ergebnis kann man mit 44.6 kHz Daten sampeln (separate FreeRTOS Task, runtergebremst damit der
|
|
belegte Core nicht komplett monopolisiert wird ...). Als Blockgröße habe ich
|
|
2048 Meßwerte gewählt. Das Mikrofon leistet 50 Hz bis 20kHz lt. Datenblatt, d.h. ein Teil der
|
|
Frequenzen kann unten & oben verworfen werden.
|
|
|
|
Damit das ganze hinreichend zügig abläuft, habe ich eine Verarbeitungspipeline aufgebaut:
|
|
Datenerfassung(Blockgröße) -> FFT -> Binning(10 St.). Das Sampeln wird in Lua aktiviert, die Ergebnisse
|
|
(aus zwei wechselseitig genutzten Ergebnispuffern à 10 'Bins') werden per Timertask in Lua ausgelesen
|
|
und an die LED-Kette ausgegeben.
|
|
|
|
Für die Verifizierung der Funktion habe ich zunächst mit folgender Webseite Testtöne mit definierter
|
|
Frequenz erzeugt: http://onlinetonegenerator.com/432Hz.html .
|
|
Leider zeigt sich, das die generierten Töne niedriger und vor allem der hohen Frequenzen nicht 'laut'
|
|
genug sind.
|
|
|
|
Es trifft sich glücklich, das wir im Space von hacKNology einen Funktionsgenerator zur Verfügung haben.
|
|
Wenn wir den direkt auf den ADC schalten (ohne Mikro), können wir mit der Einstellung 2,2Vpp Testsignale
|
|
für den ganzen Bereich sauber generieren (überflüssig, zu sagen, das wir den Funktionsgenerator
|
|
seinerseits vorab mittels Oszi überprüft haben ... ;).
|
|
|
|
Ein paar Meßergebnisse:
|
|
Die Aufnahme der Meßwerte benötigt ca. 45ms, das Umrechnen per FFT ca. 0,8 ms!
|
|
(Umso sinnfreier die Umsetzung in ESP32-Assembler ...).
|
|
|
|
Da ich gerade keine Schalter zur Hand hatte, habe ich zum Feintuning eine Flutter-App per Multicast
|
|
angebunden - die ich auf meinen 'NodeSwarm Devices' standardmäßig vorhalte.
|
|
|
|
## NodeSP Android App
|
|
|
|
Die App sendet Konfigurationsdaten an die NodeMCU per Multicast, am wichtigsten natürlich
|
|
für das Einstellen der Empfindlichkeit. Wenn die Umgebung sehr laut ist, sollte man runterregeln können ...
|
|
Die restlichen Parameter dienen der Konfiguration der LED-Ausgabe.
|
|
|
|
<img style="max-width: 200px"
|
|
alt="App picture"
|
|
src="NodeSP_App.png"
|
|
/>
|
|
|
|
## Showtime!
|
|
|
|
Hier mal ein (kurzes) Beispiel mit Musik:
|
|
|
|
<video width="640" height="480" controls>
|
|
<source src="//media.hacknology.de/images/NodeSP/NodeSP1.webm" type="video/webm">
|
|
Your browser does not support the video tag.
|
|
</video>
|
|
|
|
|
|
## Schwächen dieser Implementierung
|
|
|
|
Das Sampling ist nicht 'sustained rate' sondern intermittierend (wenn auch kaum spürbar).
|
|
Es gibt lt. 'ESP32 - Technical Reference Manual' eine Betriebsart (LCD-Mode -> ADC/DAC-Mode) die hier Abhilfe schaffen könnte? Mal schaun ... (ein Tipp von Wolfgang).
|
|
|
|
Eine verbesserte Version werde ich zu einem späteren Zeitpunkt mittels I2S-Mikrofon (ein Tip von Markus) nachreichen
|
|
(der ESP32 unterstützt den I2S-Bus mit DMA-Transfer - da sollte was gehen ... sagt auch das Internet :-).
|
|
|