Hallo zusammen,
kurz zu mir: Mein Name ist Christian. Ich bin Maschinenbauer, bastle
aber seit einiger Zeit mit Mikrocontrollern und bin begeistert, was man
da alles so damit machen kann. Ich konnte schon einige Projekte
erfolgreich umsetzten. Aktuell hänge ich aber seit einiger Zeit an einer
Stelle. Deshalb habe ich mich entschlossen, mich an Euch zu wenden.
Meine Applikation ist folgende: Ich möchte über einen ESP8266 einen
ADXL345 Beschleunigungssensor auslesen, diese Daten über UDP an ein
externes Programm am PC senden und dort eine FFT machen. Ich nutze zur
Programmierung die Arduino IDE. Wie so oft bei den Applikationen
funktionierte eine erste Inbetriebahme einwandfrei. Ich kann meine Daten
schicken, die Daten kommen an und ich kann die FFT berechnen. Leider kam
dann ein -für mich bisher unlösbares- Problem auf. Es zeigte sich, dass
meine FFT-Ergebnisse nicht stimmen. Basis ist ein FFT-Algorithmus, den
ich schon mehrfach eingesetzt habe und bei Referenzschwingungen korrekte
Ergebnisse zeigte. Das Problem liegt vielmehr an einer falschen, nicht
korrekten Zeitbasis. Leider widmet sich keines der Ergebnisse zu dem
Sensor einer Datenerfassung in "Echtzeit".
Meine zentrale Frage ist: Wie bekomme ich die Daten korrekt äquidistant
vom Sensor abgetastet?
Ich habe noch nie mit digitalen Sensoren gearbeitet und deshalb wären
ein paar Hinweise von Euch sehr hilfreich. Vielleicht hat der eine oder
andere Erfahrungen mit dem Sensor ADXL345. Wichtig wäre mir, ob der Eine
oder andere mich in meinem Kurz "bestätigen" oder diesen "anpassen"
könnte.
Zu meinen bisherigen Versuchen:
Meine Zielgröße ist eine Abtastrate von über 1500 Hz, deshalb nutze die
SPI-Schnittstelle, um überhaupt so hohe Raten zu erreichen. Zitat aus
dem Datenblatt:
"Use of the 3200 Hz and 1600 Hz output data rates is only
recommended with SPI communication rates greater than or equal to 2
MHz."
Der ADXL345-Sensor hat zwei Pins mit Namen INT1 und INT2. Diese Pins
können anscheinend so programmiert werden, dass sie mir das
Vorhandensein von neuen Daten anzeigen (digital, HIGH oder LOW).
Vermutlich muss ich dabei einen DATA_READY-Interrupt definieren. Hier
weiss ich leider nicht weiter, wie ich das in Code umsetzen kann.
Wenn ich richtig liege, muss ich diese Pins mit einem Eingang meines ESP
verbinden und könnte so erkennen ob neue Daten vorliegen. Für diese
Information wird dann über ein zusätzliches Kabel neben den schon
vorhandenen SPI-Kabeln übertragen?
Nun meine Frage: Meint Ihr dieser Ansatz wäre zielführend?
Wenn dem so wäre, wie würde ich das im Code umsetzen? Vor allem würde
mich interessieren, wie Ihr bei der Gesamtarchitektur vorgehen würdet.
Wären Interrupts ein Ansatz? Ich stelle mir das so vor: Zunächst muss
ich heraubekommen, welche Register ich im Sensor für die Definition des
korrekten Verhalten beschreiben muss. Ich definiere anschließend einen
normalen Interrupt in meinem Code. Wenn der Interrupt (im Takt der
Samplerate) kommt, schreibe ich in diesem meine Daten via UDP weg. Hier
habe ich schon die nächste Frage: Ich meine aber einmal gelesen zu
haben, dass in einem Interrupt keine Einleseroutinen, z.B. Serial.read
beinhalten sollten, da diese sehr lange brauchen. Könnte das bei meiner
UDP-Kommunikation auch zu einem Problem werden?
Zusammengefasst: Ich denke, meine Lösung geht nur über die korrekte
Einstellung des ADXL435.
Es wäre sehr nett, wenn Sie jemand einmal meine Problemstellung
anschauen und mir ein paar Hinweise geben könnte, wie ich das umgesetzt
bekomme.
Vielen Dank und vielleicht bis später!
Grüße
Christian
Christian schrieb:> Meine zentrale Frage ist:> Wie bekomme ich die Daten korrekt äquidistant vom Sensor abgetastet?
Das macht der ADXL435 Sensor schon von selbst. Der Sensor tastet in
äquidistanten Abständen ab, welche du als 'output data rate' im Register
BW_RATE (bits D3-D0 Rate) festlegen kannst.
> Der ADXL345-Sensor hat zwei Pins mit Namen INT1 und INT2. Diese Pins> können anscheinend so programmiert werden, (...)
Dazu musst du lediglich, wie schon von dir vermutet, die Register
INT_ENABLE und INT_MAP im Sensor setzten. Im INT_SOURCE Register kann
zusätzlich der aktuelle Status der anstehenden Interrupts abgefragt
werden.
Beispiel:
SPI_ADXL345_Write_Reg(INT_ENABLE, 0x81) // enable DATA_READY + Overrun
SPI_ADXL345_Write_Reg(INT_MAP, 0x01) // DATA_READY -> INT1 pin /
Overrun -> INT2 pin
So wird für jeden neuen Sensor-Wert der INT1 pin = high gesetzt.
Der INT2 pin = high zeigt dir gleichermassen, wenn du die Sensor-Werte
aus dem FIFO nicht schnell genug abgeholt hast und es zu einem Überlauf
gekommen ist, du also Sensor-Werte verloren hast. Womit wir bei deinem
Problem sind.
Vermutlich gehen bei dir Sensor-Werte verloren, weil
1) die SPI Kommunikation zum ADXL435 zu langsam ist (zu niedrige
Baudrate, Software-SPI, ...)
2) die UDP Kommunikation zum PC hin zu lange dauert (und z.B. in der
Zeit die SPI Kommunikation blockiert)
> Wenn der Interrupt (im Takt der Samplerate) kommt, schreibe ich in diesem meine
Daten via UDP weg.
> (...) dass in einem Interrupt keine Einleseroutinen, z.B. Serial.read> beinhalten sollten, da diese sehr lange brauchen. Könnte das bei meiner> UDP-Kommunikation auch zu einem Problem werden?
Ja, UDP Kommunikation solltest du auf keinen Fall in dem
Empfangsinterrupt implementieren.
Du solltest nicht versuchen für jeden Sensor-Wert (6 byte) ein UDP Paket
zu verschicken. Der Header des UDP Paket (8 byte) wäre damit schon
länger als deine Nutzdaten. Das Erstellen jedes UDP Paketes braucht eine
gewisse Zeit.
Sinnvoller ist es eine Anzahl von Sensor-Messwerten zu sammeln und
zusammen in einem UDP Paket zu verschicken. Als Hausnummer würde ich 100
Sensor-Messwerte (=600 byte) für ein UDP Paket ansetzten. Damit bist du
bei 32 UDP Paketen pro Sekunde und benötigst für doppelt gebufferte
Sensor-Messwerte im Controller nur 2x600 byte RAM.
Guten Abend void,
vielen Dank für Deine ausführliche Antwort. Ich werde Deine Hinweise
gleich morgen früh ausprobieren. Spontan habe ich jedoch gleich ein paar
Rückfragen:
Die Array-Thematik, die Du ansprichst, hatte ich auch schon ausprobiert,
jedoch wieder verworfen, da ich meine, dass es schnell genug ist, meine
UDP-Werte einzeln zu verschicken. Ich hatte einmal -wenn ich das richtig
gemacht habe- die Zeiten für das Zusammenstellen und Versenden eines
UDP-Paketes mit folgendem Code:
1
Udp.beginPacket(ip, recPort);
2
Udp.write(packetBuffer,UDP_PACKET_SIZE);
3
Udp.endPacket();
hier komme ich auf etwa 58 Microsekunden (gemessen mit der
micros()-Funktion) für das Versenden eines Pakets aus x-, y-, und z-Wert
(=12 Bytes). Meine "Denke" war, folgende:
Die 0,000058 sec entsprechen 17.241 Hz. Das heißt für mich doch, dass
ich theoretisch Frequenzen bis zu dieser Frequenz erfassen kann und die
Werte einzeln wegschicken könnte, wenn ich alle anderen Einflüsse
vernachlässige, oder? Wäre super, wenn Du das bestätigen oder
widerlegen könntet. Deshalb zusammengefasst: Diese Zeit sollte doch
ausreichend kurz sein, um Werte mit 3200 Hz zu erfassen, weshalb ich den
Array-Ansatz verworfen hatte. Liege ich damit richtig?
Bei dem Array-Ansatz kann ich höchstens 44 Werte wegschicken, da dann
mein Puffer in meinem externen Programm (Labview) höchstens 548 Bytes
(12 Bytes * 44 Werte = 528 Bytes). Hier war mir nicht ganz klar, wie ich
das dann mit dem Interrupt im Code verbinde (da hier das Wegschicken in
jedem Fall länger dauert, wie Du ja oben ausgeführt hast), bei einem
Wert ist mir die "Architektur" klar: In meiner Interrupt-Funktion habe
ich in diesem Fall die oben genannten Codezeilen zur UDP-Kommunikation
drin, hier dürfte -wenn ich richtig liege- nichts "aus dem Tritt
kommen". Wo bzw. wie würde ich das dann mit einem Array korrekt
umsetzen? Vielleicht hast Du mir da noch einen Tipp.
Dir in jedem Fall einmal vielen, vielen Dank dass Du mich da
grundsätzlich auf den "rechten Weg" gebracht hast. Ich probiere das
morgen früh gleich einmal aus und werde hier wieder berichten und
schicke Dir den Code.
DANKE und Grüße
Christian
Bei den ESP wird auch noch zeit benotigt u die ganse Wifi Geschichte ab
zu handelen. Leider hasst du das nicht selbst in griff : irgendwen wird
ihre Arduino sketch abgebrochen, und lauft dan Wifi. Mess mal ihre min
und max "durchlaufzeit", dan haben sie ein idee wiefiel Zeit verpasst
wird mit Wifi.
Dir auch einen schönen Abend Christian.
> (...) die Zeiten für das Zusammenstellen und Versenden eines> UDP-Paketes mit folgendem Code:>> Udp.beginPacket(ip, recPort);> Udp.write(packetBuffer,UDP_PACKET_SIZE);> Udp.endPacket();>> hier komme ich auf etwa 58 Microsekunden
Der Code scheint wirklich das UDP-Paket zu erstellen und auch zu
verschicken bis die Daten wirklich aus dem TX-Buffer des Ethernet-Stacks
raus sind. Falls jemand mitliest der ESP8266Wifi/lwIP besser kennt,
bitte korrigiert mich.
Udp.endPacket() // Blockiert bis Paket wirklich gesendet ist, liefert
return Wert 1 wenn erfolgreich.
https://www.arduino.cc/en/Reference/WiFiUDPEndPacket
Ruft dazu auf: -> _ctx->send() [1]WiFiUdp.cpp -> udp_sendto()
[1]/include/UdpContext.h
upd_sendto() [2] ist aus dem lwIP stack und macht blockierendes senden.
[1]
https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/src/
[2]
http://www.nongnu.org/lwip/2_0_x/group__udp__raw.html#gaa0e135a5958f1f0cc83cbeb609e18743
Weil aber das senden gerade über WLAN von der Signal-Qualität abhängig
ist halte ich es für unwahrscheinlich das deine gemessenen 58
Microsekunden konstant über alle Sende-Vorgänge sind. Im Gegenteil ist
anzunehmen, dass es eine stark schwankende Sende-Latenz gibt. Annahme:
99% deiner UDP Pakete haben <=60 µs Sende-Latenz, 1% haben <=10 ms.
Wenn dein Sende-Funktion einmalig für 10ms steht,
erzeugt der ADXL435 Sensor 32 neue Sensor-Werte und spätestens dann
läuft sein (32 fach) FIFO über.
Dazu kommt noch der von RP6conrad angesprochene Effekt das vermutlich
deine main loop zyklisch für Wifi Management tasks (oder anderes)
unterbrochen wird. Bzw. Arduino like, eben dies vor dem periodischen
Aufruf von loop() gemacht wird.
Diese beiden Effekte sollten sich messbar bemerkbar machen.
Das würde ich beides mal mit einem Timer ausmessen ("micros()"
Funktion).
> Die 0,000058 sec entsprechen 17241 Hz. (...)> Deshalb zusammengefasst: Diese Zeit sollte doch> ausreichend kurz sein, um Werte mit 3200 Hz zu erfassen, weshalb ich den> Array-Ansatz verworfen hatte. Liege ich damit richtig?
Ja, deine Rechnung ist richtig. Aber deine Messung vermutlich nicht
(immer).
> Wo bzw. wie würde ich das dann mit einem Array korrekt> umsetzen? Vielleicht hast Du mir da noch einen Tipp.
Ein Array mit 88 Einträgen zu je 12 byte (1056 byte = 2*528 byte =
2*44*12 byte).
Ein Zähler (oder pointer) der auf den gerade aktuell zu füllenden
Eintrag im Array verweist.
Immer wenn ein Sensor Messwert vorliegt (INT1 = high, kann auch einfach
als Pin am Controller ausgewertet werden) SPI Transfer auslösen und
Messwerte in Array eintragen an die Stelle an die der Zähler verweist
und Zähler auf nächstes freien Eintrag zeigen lassen.
Falls Zähler = Array Eintrag 45 ist UPD Paket mit Array Einträgen 1-44
verschicken
Falls Zähler = Array Eintrag 89 ist Zähler auf 0 zurück setzten und UPD
Paket mit Array Einträgen 45-88 verschicken
Deine Debugging-Möglichkeiten:
- Overrun am INT2 pin überwachen. Falls das auftritt, hast du
Sensor-Messwerte verloren (FIFO Überlauf).
- Sende und main loop() Zeiten messen.
- Mit Wireshark die Anzahl der angekommenen UDP Pakete am PC auf
Konsistenz mit Anzahl der Messwerte prüfen.
Guten Abend void(),
Dir noch einmal vielen, vielen Dank für Deine ausführlichen Erklärungen.
Diese sind auch für mich als Fachfremden nachvollziehbar (einiges davon
glaube ich nun zumindest verstanden zu haben). Mein Ziel ist nämlich,
das alles nachzuvollziehen und zu verstehen. Danke ich weiß das zu
schätzen, wir haben alle wenig Zeit.
Ich habe auf Basis Deiner Erläuterungen einmal versucht, das in Code
umzusetzen. Das wird nicht funktionieren, da ich heute Abend keinen
Arduino zum testen habe. Das ist erst mal ein Versuch, Deine
grundlegende Architektur nachzubilden, void. Morgen früh werde ich das
gleich austesten und auch mal wie von Dir vorgeschlagen, die Zeiten
"ausmessen". Mit Wireshark hatte ich mir schon bei meinen ersten
Versuchen gearbeitet. Das schaue ich mir morgen auch noch mal an.
Es wäre klasse, wenn Du mir zu meinen Versuchen noch ein paar Hinweise
geben könntest. Wichtig: Wie oben beschrieben, geht es mir nicht darum,
dass Du mir meine Code schreibst. Mir wäre es lieber, wenn Du mich auf
meine Fehler hinweisen und mir Lösungshinweise geben könntest. Natürlich
nur wenn Du Zeit und Lust (und noch Geduld mit mir) hast.
Folgende Punkte sind mir in meinem Code noch unklar (wie geschrieben, da
werden noch unzählige Fehler drin sein, aber mir geht es erst mal darum,
ob ich die Gesamtarchitektur verstanden habe):
Im Code in der jetzigen Form ln?ese ich ständig die Register in der
main-loop(), quasi "volle Pulle". Würdest Du das so auch machen, da Du
ja schreibst:
void schrieb:> Immer wenn ein Sensor Messwert vorliegt (INT1 = high, kann auch einfach> als Pin am Controller ausgewertet werden) SPI Transfer auslösen und> Messwerte in Array eintragen an die Stelle an die der Zähler verweist> und Zähler auf nächstes freien Eintrag zeigen lassen.
Meine Interrupt-Routine nutze ich nur, um in der Hauptschleife zu
erkennen, indem ich einen Zähler inkrementiere und dann detektiere, wann
die 45 Werte vorhanden sind und ich dann mein Array via UDp wegschicken
kann. Somit habe ich in meinem zeitkritischen Interrupt keine
"bremsenden" Kommunikationsaufrufe und er kann "schön" weiter mein Array
füllen. Passt das aus Eurer Sicht? So hatte ich Dich void zumindest
verstanden.
Was vermutlich auch noch nicht korrekt sein wird, ist meine Leseroutine
für die Register (readRegister). Hier bin ich mir nicht sicher, ob das
der von Dir angesprochene SPI-Transfer ist. Da muss ich morgen nochmal
ran.
Ich würde mich sehr darüber freuen, wenn Du mit mir weiter die Lösung
entwicklen könntest - bitte entschuldige meine vielleicht manchmal
dummen Nachfragen - ich gebe mir aber wirklich Mühe und versuche Dir zu
folgen
Viele Grüße und nochmal Danke
Christian
Guten Abend zusammen,
ich hatte Euch ja versprochen, dass ich mich heute noch einmal an den
Code setze und diesen unter Realbedingungen ausprobiere. Leider bin ich
nicht so weit gekommen wie ich mir gewünscht hatte, aber anbei findet
Ihr meinen -hoffentlich finalen- Code.
Was soweit funktioniert ist der "Hardware-Interrupt", getriggert vom
Sensor. Das habe ich durch ein paar Oszillopskop-Aufnahmen verifiziert.
Die eingestellten Sollfrequenzen 50, 800, 1.600 und 3.200 Hz passen mit
den Impulsfrequenzen zusammen: 49,41 / 782,4 / 1.603 und 3.145 Hz (siehe
ZIP-Ordner). Ich denke, diese Abweichungen liegen in der Toleranz, oder?
Auch der Code scheint erst einmal zu funktionieren. In der
Interrupt-Routine wird die von Dir void vorgeschlagene Laufvariable
(loopi) hochgezählt: Jedes mal, wenn der Interrupt kommt (also mit
meiner konstanten Abtastfrequenz) wird diese hochgezählt. Wenn mein
Maximum (45) erreicht ist, schicke ich mein UDP-Packet weg. Bitte
beachtet: In dieser Version sind die ganzen UDP-und Wifi-Funktionen noch
nicht berücksichtigt. Ich habe jetzt nur mal den UDP-Block
(auskommentiert) drin gelassen (siehe auch der letzte Beitrag).
1
Udp.beginPacket(ip,recPort);
2
Udp.write(packetBuffer,UDP_PACKET_SIZE);
3
Udp.endPacket();
Jetzt eine Frage an Euch: Meint Ihr, dass ich am Ziel bin? Rein vom Code
her gesehen? Vielleicht seht Ihr ja Optimierungspotenzial in Bezug auf
das Timing. Wichtigste Frage ist aus meiner Sicht doch, ob das o.g.
UDP-Paket korrekt im Code platziert ist... Ich werde morgen die Tests
machen, um dann eventuelle Buffer-Überläufe zu detektieren und mit
micros()/millis() Zeiten messen. Ich werde auch die Analysen machen, die
Du void empfohlen hast:
void schrieb:> Deine Debugging-Möglichkeiten:> - Overrun am INT2 pin überwachen. Falls das auftritt, hast du> Sensor-Messwerte verloren (FIFO Überlauf).> - Sende und main loop() Zeiten messen.> - Mit Wireshark die Anzahl der angekommenen UDP Pakete am PC auf> Konsistenz mit Anzahl der Messwerte prüfen.
Gleich mal die Frage: Was habe ich denn aber für Möglichkeiten, wenn
hier noch Verzögerungen auftauchen sollten? Mein globales Ziel ist es ja
für meine FFT absolut äquidistant abgetastete Werte zu erhalten - sonst
stimmt das alles nicht...
Ich würde mich freuen, wenn Ihr mal eine Abschätzung abgeben könntet, ob
ich mit dem Code auf dem richtigen Weg bin und ob Ihr
Verbesserungsvorschläge habt.
Ich bin gespannt auf Euer Feedback, Eure Anmerkungen und Kritik!
Vielen Dank an alle schon einmal und Grüße
Christian
Christian B. schrieb:> Was soweit funktioniert ist der "Hardware-Interrupt", getriggert vom> Sensor. Das habe ich durch ein paar Oszillopskop-Aufnahmen verifiziert. (...)> Ich denke, diese Abweichungen liegen in der Toleranz, oder?
49,41 Hz = 50 Hz -1.18%
782,4 Hz = 800 Hz -2.20%
1603 Hz = 1600 Hz +0.19%
3145 Hz = 3200 Hz -1.72%
Erstmal diese Toleranz ist erwartet. Der ADXL345 arbeitet ohne ext.
Quarz und erzeugt damit seine Frequenz durch einen internen
(Ring-)Oszillator. Eine Abweichung von +/-2..3% ist da durchaus zu
erwarten. Eine max. spezifierte Abweichung der Frequenz von der
eingestellten ODR habe ich aber leider nicht im Datenblatt gesehen.
Anzunehmen wäre aber eine ähnliche Abweichung aller deiner 4 Messungen,
weil du unter gleichen Temperatur- und
Spannungsversorgungs-Verhältnissen gemessen hast.
Die Frequenz-Anzeige deine Messungen 'Messung_1600Hz.jpg' und
'Messung_3600Hz.jpg' wird vermutlich besser/korrekter wenn du für die
Messungen noch folgendes machst:
- Tastkopf Masse anschließen (bzw. CH1 von AC auf DC umschalten)
- Trigger Level in die Mitte des Signals legen
- Trigger mode: normal (nicht auto)
- Finger weg von autoset, welcher das alles durcheinander würfelt. ;-)
> Mein Ziel ist nämlich,> das alles nachzuvollziehen und zu verstehen.
Auch wenn du nicht Fachfremd wärst, finde ich du schlägst dich bei der
Aufgabe ganz gut. Und deine Vorgehensweise finde ich methodisch und gut.
Von daher macht es Spass dir mit Feedback zu helfen.
Den Code schaue ich mir morgen nochmal an. Heute Nacht wird das nichts
mehr...
Guten Morgen void,
Dir erstmal vielen, vielen Dank für Dein Feedback. Ich freue mich
wirklich sehr, dass sich ein Profi mit meinem Problem auseinander setzt
und mit mir eine solche Geduld hat. Noch mal ein Lob: Du erklärst
wirklich super. Das habe ich in Diskussionen zwischen Elektronikern /
Elektrotechnikern & Maschinenbauern meist anders erlebt.
Ich habe jetzt schon unglaublich viel von Deinen Ausführungen gelernt
(und das ist mir das Wichtigste). Deshalb kann ich das nur zurückgeben:
Es macht echt Spaß! VIELEN DANK!
Ich hoffe, ich habe Deine Hinweise korrekt umgesetzt. Ich bin gespannt
auf Dein Feedback und werde heute im Laufe des Tages noch die gestern
geschriebenen "Analysen" machen und die Ergebnisse hier wieder
einstellen.
Nochmal: Danke, ich weiß das wirklich zu schätzen - wir haben alle wenig
Zeit.
Ich freue mich auf heute Abend, wünsche Dir einen erfolgreichen Tag und
sende Dir
viele Grüße
Christian
> Was habe ich denn aber für Möglichkeiten, wenn> hier noch Verzögerungen auftauchen sollten?
Ich denke, dass du so nicht auf einen grünen Zweig kommen wirst. Bei
meinen Experimenten habe ich ebenfalls festgestellt, dass meine eigenen
Programmteile (also die loop() Funktion) in unregelmäßigen Abständen für
ungewisse Zeit (teilweise einige hundert Millisekunden) nicht mehr
ausgeführt wird.
Ich denke, dass du daran nichts ändern kannst. Man müsste eine neue
Firmware für den Chip schreiben, die ganz anders funktioniert.
Wenn man die WLAN Schnittstelle deaktiviert, bekommt man ein besser
vorhersagbares Zeitverhalten. Aber auch dann treten Verzögerungen auf
(wegen dem Nachladen von Code aus dem Flash). Außerdem ist das Ein-/Aus
Schalten der WLAN Schnittstelle Zeitaufwändig und mit Verlust aller
Daten im RAM Verbunden, weil man den Chip dazu jedesmal neu booten muss.
Ich würde die Suche nach einer eleganten Lösung an dieser Stelle
abbrechen und lieber einen zweiten Mikrocontroller daneben setzen, der
die Zeitkritischen Aufgaben erledigt. Ich habe gelesen, dass der ESP32
zwei Mikrocontroller enthalten soll. Vielleicht kannst du das mit dem
Teil besser umsetzen.
Hallo Stefanus,
vielen Dank für Deine Antwort. Auch wenn Sie mich ehrlich gesagt sehr
frustriert hat...Ich werde jetzt mal die Antwort von void heute Abend
abwarten und mich dann noch einmal melden.
Zwei Rückfragen habe ich aber gleich:
1. Zum neuen Setup
Ich hatte ganz zu Beginn einmal mit dem Pretzelboard/NanoESP
experimentiert (https://iot.fkainka.de/board). Auf diesem Board
kommuniziert ein ESP8266 (kein 32er) mit einem Arduino Nano über die
(glaube ich sehr langsame) SoftwareSerial-Schnittstelle. Jetzt könnte
ich hier doch meine zeitkritische SPI-Datenerfassung des ADXL345 auf den
Nano "umbauen", um dann -nicht mehr zeitkritisch- die Daten "gemütlich"
wegzuschicken, wenn das Paket voll/da ist. Habe ich Dich da richtig
verstanden? Grundsätzlich bekomme ich das bestimmt hin, aber...Die Daten
sollten quasi in "Echtzeit" in meiner GUI in Labview dargestellt werden
(das heißt, es sollten keine Verzögerungen auftauchen und das Event
direkt angezeiggt werden). Schließt sich das nicht aus (schnelle Anzeige
und langsame Schnittstelle)? Ich hoffe, Du verstehst was ich meine...
2. zum alten Setup
Ich hatte einmal etwas von einem SPIFFS-File-System gehört. Wäre das
noch eine Idee, um mein derzeitiges Setup zu retten? Ich denke
folgendes: Wenn ich die (ja jetzt offensichtlich äquidistant
abgetasteten) Daten korrekt erfasse, speichere ich mein Array wiederum
irgendwie in einen SPIFFS-Puffer. Wenn dann in der loop() Zeit ist
(wobei ich mich dann gleich wieder frage, wie ich das detektiere...)
schicke ich die Daten via UDP weg.
Wie gesagt, das erst einmal als erste (frustrierte) Rückfrage :-(
Dir in jedem Fall vielen Dank für Deine Einschätzung
Ich melde mich
Grüße
Christian
Wegen dem Pretzel Board: Ja, damit dürfte die Aufgabe ohne größere
Probleme lösbar sein. Das Board ist aber ziemlich teuer. Wenn du lieber
eine eigene ähnliche Schaltung verwenden willst, schau mal von meinem
projekt ab: http://stefanfrings.de/wlan_io/index.html
Diese Schaltung + Software wurde wochenlang erfolgreich auf Stabilität
geprüft. Ist dem Pretzel-Board ganz ähnlich bis auf den wesentlichen
Unterschied, dass die ESP Kommunikation mit den Standardmäßigen 115200
Baud abläuft. Somit ist es nicht nötig, den ESP vor dem Einbauen auf
9600 Baud umzukonfigurieren.
Deine Forderung nach "keiner Verzögerung" ist technisch nicht umsetzbar.
Wenn es gut läuft, hast allein im WLAN netz schon um 10ms Verzögerung.
Wenn es schlecht läuft, können es einige hundert ms sein. Da hast du mit
WLAN die denkbar schlechteste Wahl getroffen.
Andererseits kann das Menschliche Auge ohnehin nur wenige Bilder pro
Sekunde wahrnehmen. Deswegen würden mich die Verzögerungen durch das
WLAN nicht stören. Man muss das Ganze nur so programmieren, dass
unregelmäßige Verzögerungen nicht zu Fehlfunktionen führen. Jeder Audio-
und Video-Streaming Player demonstriert, dass das machbar ist. Und zwar
mit etwas Pufferspeicher und notfalls dem Verwerfen von Frames.
> Schließt sich das nicht aus (schnelle Anzeige und langsame Schnittstelle)?
Dein WLAN und der ESP sind zumindest im Mittel sicher erheblich
schneller, als dein Auge. Und wenn es mal hakelt, friert die Anzeige
halt mal kurz ein, lässt ein paar Updates aus und fängt sich dann
wieder.
Dein Hauptproblem ist, dass der ESP zeitweise aufhört, zu messen. Dan
kannst du mit einem zweiten Mikrocontroller leicht lösen. Der muss
nämlich nicht stehen bleiben, während der ESP mit sich selbst oder dem
Netz beschäftigt ist.
> Ich hatte einmal etwas von einem SPIFFS-File-System gehört. Wäre das> noch eine Idee, um mein derzeitiges Setup zu retten?
Da wird der Flash Speicher zum Speichern von Daten verwendet. Der
verschleißt aber schnell. Wenn du vor hast, die Speicherplätze mehr als
100 mal zu überschreiben, informiere dich vorher, ob dieses SPIFFS
Filesystem Wear-Levelling beinhaltet. Denn wenn es nicht dabei ist, dann
vergiss es gleich wieder.
Mir ist allerdings unklar, wie du damit dein Setup retten willst.
Ich würde einen zweiten µC zum Messen und Puffern benutzen. Der ESP
dient dann nur noch als Netzwerk-Schnittstelle. Egal ob er läuft oder
nicht, die Messung findet trotzdem fortlaufend statt und füllt den
Puffer. Ein zweiter Thread überträgt den aktuellen Stand aus dem Puffer
in regelmäßigen Intervallen zum PC. Wenn das dort genau so regelmäßig
ankommt ist alles gut. Wenn nicht, hat das keinen Einfluss auf das
Timing der Messung.
Auch SPIFFS auf der ESP hat das gleiche problem : irgendwo lauft die
WLAN Geschichte und blokkiert dan die loop(). Ich verwende das bei eine
serial datalogger, aber ab und zu werden Daten verloren gehen. Ich
sollte eine andere Schnittstelle versuchen, mit WLAN hasst du immer
diese Verzogerungen. Auch eine andere µcontroller scheint mir besser :
STM32 hat sehr billige und gute boards. Teilweise sind die auch mit die
Arduino Umgebung zu programmieren. Aber auch eine Umstieg nach GCC und
standard C scheint mir sinnfol. Arduino hat relatif fiel "overhead", und
fur Zeitkritische Sachen ist das nicht ideal.
Guten Abend,
ich kann Euch etwas Positives berichten. Es funktioniert...! Ich habe
mal ein paar "Messergebnisse" beigefügt. Für unsere Zwecke ist das
völlig ausreichend. Die Frequenzen stimmen sehr gut! Das seht Ihr anbei.
Die Dateinamen entsprechen jeweils den eingestellten Frequenzen. Das
passt alles soweit, nur...
...sehen meine Signale leider sehr schlecht aus...Die FFT stimmt zwar
(da der Sensor auf die Schwingungen reagiert), aber nur mit
Impulsfolgen, nicht mit einem "schönen" Sinus, wie es sein sollte. Ihr
seht das auf den Abbildungen (die rechte Spalte sind jeweils die
verrauschten Zeitsignale, links die FFT - bitte nicht von den
Achsbeschriftungen beeirren lassen, die stimmen noch nicht).
ch habe etwas experimentiert und ich bin mir mittlerweile fast sicher,
dass es mit den Datentypen zusammenhängt (Der aktuelle Sketch liegt
bei). Dieser hat sich nur teilweise geändert). Am "besten" wird das
Signal, wenn ich meine Daten folgendermaßen übergebe (Datei
Spikes_Sinus.jpg):
1
xVal=(int16_t)((((int)values[1])<<8)|values[0]);
2
yVal=(int16_t)((((int)values[3])<<8)|values[2]);
3
zVal=(int16_t)((((int)values[5])<<8)|values[4]);
Die schlechten Signale rühren daher, dass alle drei Werte in allen
Achsen 0 sind - besonders gut zu erkennen in der Datei Spikes_3.jpg -
alle drei Achsen zeigen plötzlich einen Werte von 0...?
Mache ich das mit dem Einlesen der "Rohwerte" richtig? Ürsprünglich
hatte ich diese Werte, direkt meinem Array zugewiesen (seht Ihr im Code,
im auskommentierten Teil), da war das Signal aber noch
verrauschter...Kann das daran liegen, dass ich die Werte über ein Array
mittels UDP übertrage:
1
Udp.write((byte*)&accels,sizeof(accels));
Witzigerweise reichen diese Impulsfolgen aus, um die korrekte Frequenzen
zu berechnen. Zu Erregung habe ich den Sensor mit einer vorgegebenen
Frequenz beaufschlagt.
Nun meine Frage: Seht Ihr hier eine Fehler? Dazu müsst Ihr vielleicht
noch wissen, wie die Daten bei mir im Labview ankommen: Als Sting, den
ich in ein vorzeichenloses Array konvertieren muss. Dieses besteht für
jeden Messwert aus 4 Werten, z.B. 128 0 0 0, das ganze für x,-y- und
z-Achse macht dann 12 Werte (12 Bytes) für jeden Messwert (aus allen
drei Achsen). Dieses Array muss ich dann in Labview aufwendig wieder in
die einzelnen korrekten Werte zerlegen. Ich habe bis jetzt gerade mein
Labview-Programm nachvollzogen und bin zu dem Schluss gekommen, dass die
Werte schon aus dem Controller so rübergeschrieben werden. Das komische
ist jedoch, dass hier keine "Meldung" kommt:
Das hat mich wiederum stutzig gemacht...Mein Gefühl sagt mir, dass das
"irgendwie" mit den besagten Datentypen (des Structure-Array?)
zusammenhängt.
Es wäre super, wenn Ihr mir noch einmal einen Hinweis geben könntet. Ich
bin nahe dran, denke ich...Mit Euer Hilfe kann ich das vielleicht doch
noch schaffen.
Bitte entschuldigt, wenn ich diesen Sachverhalt nicht fachliche korrekt
und sauber erklären kann. Ich hoffe, die Bilder und der Code erklären
alles.
Ich würde mich über ein Feedback sehr freuen. Vielen Dank noch einmal an
alle, die hier mitdiskutiert haben (RP6conrad & Stefanus) - ohne Euch
hätte ich es nicht bis hierher geschafft! Das gilt vor allem für Dich
void!!!
Danke schon einmal, viele Grüße und vielleicht bis später
Christian
Erstmal vielen Dank Christian, ich hatte einen erfolgreichen Tag. ;-)
Und auch Dank an Stefanus der die praktische Erfahrung mit dem ESP8266
bzw. dem ESP8266Wifi Ethernet-Stack mitbringt, die mir fehlt. Ich habe
auf diesem µC bisher nur die Tasmota firmware mit MQTT verwendet. Daher
auch mein Interesse an dem Thema.
Stefanus F. schrieb:> Bei meinen Experimenten habe ich ebenfalls festgestellt, dass meine> eigenen Programmteile (also die loop() Funktion) in unregelmäßigen> Abständen für ungewisse Zeit (teilweise einige hundert Millisekunden)> nicht mehr ausgeführt wird.
Das ist genau der angesprochene Effekt. Bei Verzögerungen von teils
mehreren 100ms ist es nicht realistisch den ADXL435 Sensor direkt vom
ESP8266 auszulesen. Schon für 100ms würde ein 10x so großer FIFO im
ADXL435 benötigt. Von daher ist der Vorschlag von Stefanus absolut
richtig.
Stefanus F. schrieb:> Dein Hauptproblem ist, dass der ESP zeitweise aufhört, zu messen. Dann> kannst du mit einem zweiten Mikrocontroller leicht lösen. Der muss> nämlich nicht stehen bleiben, während der ESP mit sich selbst oder dem> Netz beschäftigt ist.
Praktisch bedeutet der Vorschlag, dass der zweite µC für die Messung in
Echtzeit und als vergrößerter FIFO fungiert. Bei der Datenrate des
Sensors von 38,4 kB/s (=12 Byte * 3200 Werte/s) wirst du mit dem von
Stefanus vorgeschlagenen Board mit ATmega644 (4kB RAM) ca. 100ms lang
Daten im RAM zwischenspeichern können. Sprich du wirst bei >100ms
blockieren des ESP8266, dann auch wieder Ausfälle bekommen, aber das
kommt dann vermutlich nur selten vor und ist akzeptabel.
Wahlweise kannst du einen zweiten µC mit mehr RAM nehmen. Klassisches
Optimierungsproblem.
Noch zwei Worte zum Datenformat.
Falls die Anzahl der Messwerte für die FFT welche du am PC machen
möchtest nicht zu hoch ist, solltest du versuchen die Messwerte als ein
Paket (oder eine durchnummerierte Anzahl von Paketen) zu versenden und
alle Daten zu verwerfen wenn es während des Messens der Sensor-Werte für
das Paket zu einem Overflow kam. Das garantiert, dass die Messdaten
innerhalb eines Paketes immer äquidistant abgetastete Werte sind. Eine
FFT über ein Paket ist damit immer korrekt. Über die Grenzen von Paketen
aber nicht gesichert (wenn Pakete nicht angekommen sind/verworfen
wurden).
Die Darstellung der x/y/z Werte würde ich erst am PC (falls notwendig)
umwandeln.
Dein Code unten dazu ist zwar korrekt, aber wandelt im besten Fall "3x
2x 8bit" über "3x 16bit" nach "3x 2x 8bit". Also 6 byte in 6 byte evtl.
mit anderer Endianess.
P.S.: Irgendwie bin ich bis hierhin von 12byte pro x/y/z Messwerte-Tupel
ausgegangen. Es sind aber nur 6byte (DATAX0, DATAX1, DATAY0, DATAY1,
DATAZ0, DATAZ1) oder habe ich was übersehen?
1
structdirs{
2
intx;
3
inty;
4
intz;
5
};
6
//(...)
7
structdirsaccels[44];// UDP-Buffer in Labview is 548 Bytes/ 12 Bytes = 44 (per sent packet):
8
// x | y | z --> 0 0 0 0 | 0 0 0 0 | 0 0 0 0
9
accels[loopi].x=((int)values[1]<<8)|(int)values[0];//The X value is stored in values[0] and values[1].
10
accels[loopi].y=((int)values[3]<<8)|(int)values[2];//The Y value is stored in values[2] and values[3].
11
accels[loopi].z=((int)values[5]<<8)|(int)values[4];//The Z value is stored in values[4] and values[5].
12
//(...)
13
Udp.write((byte*)&accels,sizeof(accels));
Zu deinem Code.
Der INT1 (DATA_READY) vom ADXL435 Sensor ist so wie ich das lese solange
high, wie noch Mess-Daten im FIFO liegen. Das spricht dafür dieses
Signal gar nicht als Interrupt auszuwerten, sondern per Pollen des Pins.
Aber als (Level) Interrupt geht natürlich auch.
Pseudocode:
Mein Vorschlag, welchen du nicht im Code abgebildet hast, war dass du
den Buffer (das Array) zweiteilst.
Eine Hälfte wird jeweils gerade befüllt, die andere als ein Paket
verschickt. Weil du jetzt die Messung in einen zweiten µC verlegst, wird
es darin vermutlich Sinnvoller sein ein Ringbuffer zu implementieren.
Oh, etwas spät. Das überlappt sich jetzt mit deiner Erfolgsmeldung.
> ich bin mir mittlerweile fast sicher, dass es mit den Datentypen zusammenhängt
Für DATA_FORMAT = 0xB liefert der Sensor
rechts-alignte, vorzeichenbehaftete 13bit Werte welche auf 16bit sign
extended werden.
Die landen dann in integer (int) Variablen. Int ist bei ESP8266 ein
signed 32bit Datentyp. [1]
Besser wäre dediziert den int16_t Typ zu verwenden, wenn du signed 16bit
brauchst.
Bzw. bei deiner Schieberei bei der du die zwei bytes in denen der 16bit
sign extended Wert drin steht auch als uint8_t behandelst. Sonst
passieren noch lustige Umwandelungen, die ich mir nach Mitternacht
gerade nicht mehr vorstellen kann.
uint8_t values[6];
struct dirs{
int16_t x, y, z;
};
int16_t xVal, yVal, zVal = 0;
xVal = (int16_t)((((uint16_t)values[1]) << 8) | (uint8_t)values[0]);
[1]
https://github.com/esp8266/Arduino/blob/master/tools/sdk/include/c_types.h
Um dem Vorsprung von Stefanus an Praxiserfahrung einhalt zu gebieten ;-)
und selber was zu lernen, habe ich heute Abend zum Test einfach mal das
Setup von Christian nachgestellt.
hw: Wemos D1 mini(ESP8266) -> 2.4GHz WiFi AccessPoint(TL-WR802N) ->
Switch(100MBitEth) -> Linux-PC
sw: SPI_Interrupt_UDP_Beta.ino, modifiziert mit loopi++ in jeder loop(),
weil keine hw die INT1 erzeugt vorhanden ist.
settings: arduino 1.8.5 / esp8266 for arduino v2.4.1 / lwIP v2
LowerMemory
Ergebnis:
Sendet UDP Paket mit 528 Byte payload im Mittel alle ~5ms, also etwa 825
kbps (~6 MB/min) Datendurchsatz.
Innerhalb von einer Minute (12000 Pakete) benötigten 52 Pakete (~0.5%)
mehr als 10ms.
Die Verteilung der Paket-Empfangsdauer ist Anhang gezeigt.
Für Echtzeitmessungen mit harten Anforderungen wird das so also nichts.
Aber wenn man hier und da mal ein paar Daten verlieren kann das durchaus
okay sein.
Ich denke mal Christian kommt jetzt von selber weiter. Und ich wende
mich dann mit dem Gelernten wieder der Anwendung als Haussteuerung mit
MQTT zu.
Guten Abend zusammen,
ich möchte Euch berichten, wie bei mir derzeit der Stand ist. Zunächst
nocheinmal Danke an alle, die mich bis hierher geführt haben. Ich hatte
ja in meinem letzten Post von einem Erfolg berichtet. Leider trifft dies
nur auf die FFT zu...Ich hätte mir wirklich gewünscht, void hätte
vollkommen recht:
void schrieb:> Ich denke mal Christian kommt jetzt von selber weiter.
Vieleicht kurz vorab als Feedback zu Euren Anmerkungen:
void: Dass da nicht alle Werte ankommen, habe ich registriert und
"akzepiert". Für unsere Anwendung ist das aber absolut ausreichen. Das
sind keine Echtzeitanforderungen. Hauptsache, die FFT stimmt. Danke für
Deine anschauliche Erklärung Deiner Messung!
Stefauns:
Die Sache mit dem Pretzelboard und Deinem Ansatz ist mir nicht mehr aus
dem Kopf. Deshalb habe ich den nächsten Sensor - einen Dehnmessstreifen
mit Verstärkerplatine von Sparkfun
https://www.sparkfun.com/products/13879 über dieses Konzept
angeschlossen.
Zusätzlich zu dem Beschleunigungssensor muss ich eine Kraftmessung
mittels DMS implementieren. Dazu habe ich mal den von Dir Stefanus
beschriebenen Ansatz angegangen: Ich habe einen zusätzlichen Controller
(Arduino Nano), den ich über die SoftwareSerial()-Schnittstelle anbinde.
Das funktioniert auch soweit. Ich bekomme die Daten der Dehmessstreifen
ausgelesen. Der Arduino ist dabei mein Sender und sendet die Daten an
den ESP (Der Sender ist: softwareSerial_HX711_Arduino_final.ino) Ich
glaube jedoch, dass mein Code nicht optimal ist. Dazu mal meine
Gedanken:
1. Die SoftwareSerial-Schnittstelle ist nicht gerade schnell, oder? Wäre
es hier besser auf die Hardware-Serielle-Schnittstelle zu gehen? Das war
urspünglich mein Ansinnen, ich bin da aber an der Pinbelegung meines
NodeMCU gescheitert.
2. Ich lese jetzt gerade ständig von der seriellen Schnittstelle in
meiner Hauptschleife mittel des folgendes Codes:
1
recvWithStartEndMarkers();
2
3
if(newData==true){
4
//Serial.print("This just in ... ");
5
//Serial.println(receivedChars);
6
DMS_value=atoi(receivedChars);
7
//Serial.println(receivedChars);
8
//Serial.println(DMS_value);
9
newData=false;
10
}
Meint Ihr, das ist ok (von den Geschwindigkeiten/Verzögerungen her)?
Oder soll ich besser noch eine if-Abfrage einbauen und wirklich nur zu
bestimmten Zeitpuntken lesen (z.B. nach 5 Sekunden), da die DMS ja nicht
zeitkritisch ist...? Ich hatte auch das ursprünglich so geplant, aber
habe es irgendwie nicht hinbekommen, wenn Ihr nun sagt, dass das besser
ist, muss ich das noch einmal versuchen...
3. Ich habe das Gefühl, dass mein Code durch die Implementierung des
zusätzlichen Controllers nicht mehr so stabil ist. Das äußert sich
dadurch, dass die Verbindung manchmal abreißt, was mir davor nie
passiert ist. Meint Ihr, das liegt an der SoftwareSerial-Schnittstelle,
oder an Unzulänglichkeiten meines Codes?
4. Wie würdet Ihr das denn grundsätzlich angehen? Meine Dehnmessstreifen
kann ich ja prinzipiell irgendwann wenn Zeit ist abfragen. Mit
grundsätzlich meine ich: Wie frägt man in einer mehr oder minder
zeitkritischen Applikation (damit meine ich meine bisherige
Beschleunigungssmessung) "nebenher" einen unzeitkritischen Wert ab, bzw.
allgemeiner, wie stellt man sicher, dass diese Abfrage nicht die höher
priorisierte Applikation ausbremst...?
Es wäre wunderbar, wenn Ihr mir noch den einen oder anderen Denkanstoß
geben könntet, damit ich auch diese Hürde packen kann. Wie Ihr merkt,
gebe ich wirklich alles, um Eure Hinweise aufzunehmen und umzusetzen.
Ich würde mich sehr über die Weiterführung der Diskussion freuen.
Vielleicht bis später!
Euch einen schönen, warmen Abend
Grüße
Christian
> Die SoftwareSerial-Schnittstelle ist nicht gerade schnell, oder?
Deswegen nutzt mein Konzept Software-Serial nur zum Senden und das geht
durchaus ganz problemlos mit 115200 Baud. Für den Empfang teilen sich
USB Port und ESP eine gemeinsame Leitung.
> Ich habe das Gefühl, dass mein Code durch die Implementierung> des zusätzlichen Controllers nicht mehr so stabil ist
Wildes Raten ist hier nicht hilfreich. Was wir auf Basis von so wenig
Informationen Meinen, ist irrelevant.
> Wie würdet Ihr das denn grundsätzlich angehen?
Soweit möglich mit State Machines (Endlicher Automat, Zustandsautomat).
Dazu Interruptroutinen nur wenn nötig und nur so klein wie nötig.
Interruptroutinen sollen Ereignisse erfassen und Daten puffern. Die
Verarbeitung der Daten sollte außerhalb der Interruptroutinen erfolgen.
Es gibt immer Ausnahmen, dennoch empfehle ich das als Grundregel.
Unerwartete Verzögerungen ermittelst du am einfachsten mit Hilfe eines
oder mehrerer I/O Pins, die du an einen Logic Analyzer anschließt. Mit
diesem Gerät kannst du über längeren Zeitraum aufzeichnen, wann welcher
Ausgang seinen Pegel geändert hat.
Serielle Ausgaben sind auch ganz nett, vor allem zusammen mit dem Wert
eines fortlaufenden Millisekunden-Zählers. Allerdings kostet die
Umwandlung in Text und die Übertragung viel Zeit, was in deinem Fall
wohl die ganze Messung zunichte machen würde.
Hallo Stefanus,
vielen Dank für Deine erneuten Hinweise. Ich habe mir mittlerweile auch
einmal Deine Homepage angeschaut. Wirklich genial, was Du alles kannst -
da bin ich wirklich beeindruckt...
Dürfte ich Dich noch einmal behelligen, bzw. ein paar Rückfragen
stellen?
Du nutzt grundsätzlich die Basis-AT-Befehle und nicht die
UDP-Bibliotheken wie ich, um die Werte zu versenden, oder? Basis für die
UDP-Übertragung ist dann die SoftwareSerial-Schnittstelle. Wenn ich
Deinen Ansatz richtig verstehe, nutzt Du die serielle Schnittstelle
tatsächlich nur um die Werte zu senden, oder? Dein Ansatz bedeutet
demnach, dass Du die Daten "bequem" in einem System (auf einem Board)
erfasst und diese dann bei Gelegenheit wegschickst. In meinem Fall würde
das bedeuten, dass ich -sobald mein Array voll ist- über die AT-Befehle
mein Array wegschicke, meinst Du folgender Pseudo-Code passt im Groben?
ifloopi==44{//(wenn der 44. Interrupt angekommen ist)
28
if(Serial.available())
29
30
sendCom("<x;y;z;DMS>");// Schreibe das Array-Paket auf die UDP
31
32
loopi=0;// Reset löscht das Array
33
34
}
35
36
}
37
38
StringsendCom(Stringcommand)// Hier übergebe ich mein Array
39
{
40
esp8266.println(command);
41
}
Dier nutze ich doch dann die langsame SoftwareSerial-Schnittstelle nur
zu bestimmten Zeiten, was für mich doch wichtig wäre, oder? Meine
Hauptschleife (zur Erfassung der Messwerte), läuft mit maximaler
Geschinwdigkeit. Prinzipielö bin ich dann doch bei meinem gestrigen
Ansatz, nur dass ich keine zusätzlich Kommunikation zur
Messwerterfassung (der DMS-Werte) benötige, sondern "nur" eine zum
Versenden über Wifi, bin ich richtig?
Es wäre super nett, wenn Du mir o.g Ansatz einmal bestätigen oder
"widerlegen" könntest, dann würde ich das tatsächlich einmal mit einem
Pretzelboard angehen (so eines habe ich hier zum testen) --hoffentlich
dann mein finaler Ansatz!
Ich bedanke mich vielmals für Deine ausführlichen Erklärungen!
Viele Grüße
Christian
> Du nutzt grundsätzlich die Basis-AT-Befehle
Nein, ich nutze in genau einem Projekt die AT Befehle. Alle anderen habe
ich mit Arduino Firmware gemacht. Ich bin der AT Firmware sehr gut
zurecht gekommen. Mein I/O Modul habe ich wochenlang auf Stabilität
geprüft, da ich es in Kombination mit einer Alarmanlage nutzen wollte.
Von der Stabilität der Firmware bin ich (inzwischen) überzeugt.
> Wenn ich Deinen Ansatz richtig verstehe, nutzt Du die serielle> Schnittstelle tatsächlich nur um die Werte zu senden, oder?> Dein Ansatz bedeutet demnach, dass Du die Daten "bequem" in> einem System (auf einem Board) erfasst und diese dann bei> Gelegenheit wegschickst.
Vermutlich meinst du meine I/O Firmware. Ich nutze dort eine Tx Leitung,
um per Software-Serial Kommandos an das ESP Modul zu senden. Empfangen
tue ich jedoch mit dem echten seriellen Port, der wechselweise auch für
die USB-UART Schnittstelle genutzt wird.
Der Mikrocontroller kann abwechselnd seriell über das ESP Modul mit dem
PC Kommunizieren und in den Pausen dazwischen andere Aufgaben
wahrnehmen. In meine I/O Firmware hat der PC die Rolle das Masters. Er
sende Kommandos an den µC, die dieser beantwortet.
Bei so einem Datensammler wie deinem scheinen mir UDP Pakete, die der µC
unaufgefordert an den PC sendet allerdings besser geeignet. Also so wie
du das oben skizziert hast. Da oben Fehlt ein bisschen Code, der den
nötigen AT Befehl erzeugt und die Zahlen darin einfügt. Das müsste mit
sprintf() relativ einfach gehen.
Du solltest dein Programm so gestalten, dass die serielle Kommunikation
nicht das Timing deiner Messungen beeinflusst. Das ist bei Softserial
ziemlich Tricky, denn SoftSerial muss zeitweise alle Interrupts sperren
(damit ihr Timing stimmt).
Ich denke, du wirst am Besten klar kommen, indem du die CPU Zeit
Scheibchenweise aufteilst. Es wird immer abwechselnd Zeit zum Messen und
Zeit zum Senden haben.
Ganz grob so:
1
unsignedlongtime;
2
3
voidsetup()
4
{
5
time=millis();
6
}
7
8
voidloop()
9
{
10
if(millis()-time>=10)
11
{
12
time+=10;
13
messen();
14
senden();
15
}
16
}
Die Funktion messen() liest den Sensor aus. Die Funktion senden() sendet
gemessene Daten, falls genügend Messungen angesammelt wurden. Beides
passiert in diesem Fall im 10ms Intervall. Damit das klappt, dürfen die
beiden Funktionen zusammen nicht länger als 10ms dauern. Ich denke, das
kann man schaffen.
Mit folgendem Code kannst du soft-serial bis zu 115200 Baud senden, das
ist in deinem Fall besser als die lahme Soft-Serial von Arduino:
1
#include<util/delay.h>
2
#include<avr/pgmspace.h>
3
#include<avr/interrupt.h>
4
5
#define F_CPU 16000000
6
#define SERIAL_BITRATE 115200
7
8
// Calculate the time per bit minus 5 CPU cycles
9
#define US_PER_BIT 1000000/SERIAL_BITRATE-5/F_CPU
10
11
#if F_CPU/SERIAL_BITRATE<100
12
#warning SERIAL_BITRATE is to high for this CPU clock frequency
// Send a char array from program memory, use with PSTR().
58
voidsoftSerial_sendStr_P(PGM_Ptext)
59
{
60
charc;
61
while((c=pgm_read_byte(text)))
62
{
63
softSerial_sendChar(c);
64
text++;
65
}
66
}
Empfangen kannst du damit nichts. Zum Empfangen musst du einen echten
seriellen Port benutzen. Ich würde in deinem Fall jedoch einfach gar
nichts empfangen. Eine simple unidirektionale Kommunikation vom µC zum
ESP Modul nach dem Fire&Forget Prinzip sollte prima funktionieren.
Bei 115200 Baud kannst innerhalb von 10ms jeweils einen AT+CIPSEND
Befehl plus etwa 80 Byte Nutzdaten senden. Du hast aber etwas weniger
Zeit, wegen dem Auslesen des Sensors. Kannst ja mal ausrechnen, ob das
deiner Anforderung genügt.
Ansonsten gibt es natürlich auch µC mit zwei echten seriellen Ports, zum
Beispiel den ATmega328PB (mit B!). Ich bin nicht sicher, ob der von
Arduino unterstützt wird, aber wenn ja, dann würde dies die
Programmierung drastisch vereinfachen. Denn echte serielle Ports können
bidirektional mit Puffer kommunizieren. Der µC kann daher andere
Programmteile ausführen, WÄHREND er gleichzeitig mit dem ESP
kommuniziert. Bei meinem obigen Ansatz mit Softserial geht das nur
abwechselnd.
Egal ob mit SoftSerial oder einem echten seriellen Port: Wenn du nicht
auf die Antwort des ESP Modul wartest, kann dein Programm sinnvolle
Dinge tun. Egal ob die WLAN Verbindung steh, hakelt oder der ESP aus
sonstigen Gründen mal gerade eine Denkpause einlegt. Wenn er länger als
10ms hängt (bzw weclhes Intervall auch immer du programmierst), gehen
allerdings einzelne UDP Pakete verloren.
Hallo Stefanus,
sorry, ich bin es nochmal: Ich schaue gerade nach dem
"Pretzelboard-Ansatz" und bin wieder auf ein Problem gestoßen: Mein
Sensor benötigt -wenn ich mit 1600 Hz abtasten möchte- eine
SPI-Kommunikation. Jetzt habe ich mir mal das Pinout-Diagramm des Boards
angeschaut und glaube, dass ich das dann gar nicht kann, da die Pin 12
und 11 vom ESP belegt sind...:-( sehe ich das richtig?
https://iot.fkainka.de/pinout-pretzel-board-nanoesp
Ich denke, wenn das so ist, dass ich mich nun entscheiden muss, ob ich
mir einen alternativen Sensor aussuche, oder das derzeitige Setup so
nehme wie es ist...Mit dem neuen Sensor müsste ich ja wieder zurück an
den Anfang :-(
Sorry, dass ich Dich noch einmal nerve - das Thema betrifft ja nur am
Rande den Betreff, bzw. das Thema über das wir gerade diskutieren. Ich
bin wirklich am Verzweifeln...
Vielen Dank Dir und Grüße
Christian
Falls du dieses ATMgea328PB Board bestellst, denke daran, dass du es
irgendwie mit dem PC verbinden willst. Du brauchst wohl auch noch einen
USB-UART dazu und eventuell einen ISP Programmer (falls es keinen
Bootloader enthält).
Mir ist noch ein Produkt eingefallen.
Arduino Pro Micro in der 3,3V Version. Der hat nur einen seriellen Port,
was ausreicht weil dort die USB Schnittstelle im AVR Mikrocontroller
integriert ist.