diff --git a/.gitignore b/.gitignore
index 6c7b69a01563fb50c37a0536766f0086656c511b..fb4390bb88d0d8e2d8555c4adf1ab184a2badd1e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
 .gitignore
+.pio
+.vscode
diff --git a/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/include/README b/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/include/README
new file mode 100644
index 0000000000000000000000000000000000000000..194dcd43252dcbeb2044ee38510415041a0e7b47
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/include/README
@@ -0,0 +1,39 @@
+
+This directory is intended for project header files.
+
+A header file is a file containing C declarations and macro definitions
+to be shared between several project source files. You request the use of a
+header file in your project source file (C, C++, etc) located in `src` folder
+by including it, with the C preprocessing directive `#include'.
+
+```src/main.c
+
+#include "header.h"
+
+int main (void)
+{
+ ...
+}
+```
+
+Including a header file produces the same results as copying the header file
+into each source file that needs it. Such copying would be time-consuming
+and error-prone. With a header file, the related declarations appear
+in only one place. If they need to be changed, they can be changed in one
+place, and programs that include the header file will automatically use the
+new version when next recompiled. The header file eliminates the labor of
+finding and changing all the copies as well as the risk that a failure to
+find one copy will result in inconsistencies within a program.
+
+In C, the usual convention is to give header files names that end with `.h'.
+It is most portable to use only letters, digits, dashes, and underscores in
+header file names, and at most one dot.
+
+Read more about using header files in official GCC documentation:
+
+* Include Syntax
+* Include Operation
+* Once-Only Headers
+* Computed Includes
+
+https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
diff --git a/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/lib/README b/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/lib/README
new file mode 100644
index 0000000000000000000000000000000000000000..6debab1e8b4c3faa0d06f4ff44bce343ce2cdcbf
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/lib/README
@@ -0,0 +1,46 @@
+
+This directory is intended for project specific (private) libraries.
+PlatformIO will compile them to static libraries and link into executable file.
+
+The source code of each library should be placed in a an own separate directory
+("lib/your_library_name/[here are source files]").
+
+For example, see a structure of the following two libraries `Foo` and `Bar`:
+
+|--lib
+|  |
+|  |--Bar
+|  |  |--docs
+|  |  |--examples
+|  |  |--src
+|  |     |- Bar.c
+|  |     |- Bar.h
+|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
+|  |
+|  |--Foo
+|  |  |- Foo.c
+|  |  |- Foo.h
+|  |
+|  |- README --> THIS FILE
+|
+|- platformio.ini
+|--src
+   |- main.c
+
+and a contents of `src/main.c`:
+```
+#include <Foo.h>
+#include <Bar.h>
+
+int main (void)
+{
+  ...
+}
+
+```
+
+PlatformIO Library Dependency Finder will find automatically dependent
+libraries scanning project source files.
+
+More information about PlatformIO Library Dependency Finder
+- https://docs.platformio.org/page/librarymanager/ldf.html
diff --git a/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/platformio.ini b/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/platformio.ini
new file mode 100644
index 0000000000000000000000000000000000000000..8aee3f8409f7d237a786c4c5e2ee2a67547dd416
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/platformio.ini
@@ -0,0 +1,38 @@
+; PlatformIO Project Configuration File
+;
+;   Build options: build flags, source filter
+;   Upload options: custom upload port, speed and extra flags
+;   Library options: dependencies, extra library storages
+;   Advanced options: extra scripting
+;
+; Please visit documentation for the other options and examples
+; https://docs.platformio.org/page/projectconf.html
+
+[env:nano33ble]
+platform = nordicnrf52
+board = nano33ble
+framework = arduino
+upload_port = COM3
+build_flags =
+	-DsmnDEFBYBUILD -DsmnNANOBLE33 -DsmnDEBUG -DDegugTerminal
+;Anstelle der relativen Pfade können auch lokale Syslinks verwendet werden(siehe unten).
+;Diese aber bitte NICHT committen!
+lib_deps = 
+	..\..\..\..\libraries\BlePoll
+	..\..\..\..\libraries\environment
+	..\..\..\..\libraries\ComRingBuf
+	..\..\..\..\libraries\LoopCheck
+	..\..\..\..\libraries\MidiNotes
+	..\..\..\..\libraries\Monitor
+	..\..\..\..\libraries\nRF52840Gpio
+	..\..\..\..\libraries\nRF52840Radio
+	..\..\..\..\libraries\nRF52840Ser
+	..\..\..\..\libraries\nRF52840Twi
+	..\..\..\..\libraries\SensorLSM9DS1
+	..\..\..\..\libraries\SoaapMsg
+	..\..\..\..\libraries\StateMachine
+	..\..\..\..\libraries\nRF52840Adc
+	..\..\..\..\libraries\ProcMeas
+	..\..\..\..\libraries\EulerAngles
+    ..\..\..\..\libraries\GpioCtrl
+	;symlink://C:\Users\lenna\OneDrive\Dokumente\Git\SOAAP\libraries\SoaapComDue
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/src/SoaapBleMidiMaster.h b/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/src/SoaapBleMidiMaster.h
new file mode 100644
index 0000000000000000000000000000000000000000..9bb0445f1db6ca2ab7082d767361e1a084332643
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/src/SoaapBleMidiMaster.h
@@ -0,0 +1,127 @@
+// ----------------------------------------------------------------------------
+//                              SoaapBleMidiMaster.h
+// Beispielhafte Anwendung SOAAP / Steuerung optischer und akustischer Ausgaben
+//      Kommunikation über BLE-Funkanäle mit Bewerbungstelegrammen
+//                        P o l l i n g - M a s t e r
+// ----------------------------------------------------------------------------
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   26. April 2022
+// Letzte Bearbeitung: 15. März 2022
+//
+
+#ifndef SoaapBleMidiMaster_h
+#define SoaapBleMidiMaster_h
+
+// Vordefinitionen, Festlegungen zur Kompilierung
+//
+
+//#define DebugTerminal
+// Mit dieser Definition werden die Klasse Monitor und weitere Testmethoden
+// eingebunden, womit ein anwendungsorientiertes Debugging möglich ist
+// Diese Definition wird in der Projektumgebung des Entwicklungssystems gesetzt
+// (z.B. Arduino Compile Options bei Eclipse/Sloeber)
+
+//#define TEST001
+// Ausgaben an serielle schnittstelle zur Prüfung der ap-Zustandsmaschine
+#define testOut(x)    smnSerial.print(x)
+
+
+#include  "LoopCheck.h"
+#include  "StateMachine.h"
+#include  "nRF52840Radio.h"
+#include  "MidiNotes.h"
+#include  "BlePoll.h"
+#include  "ComRingBuf.h"
+#include  "nRF52840Ser.h"
+#include  "Monitor.h"
+
+// ----------------------------------------------------------------------------
+// Datentypen
+// ----------------------------------------------------------------------------
+//
+typedef struct _Value2Midi
+{
+  short borderLowAz;
+  short borderHighAz;
+  short borderLowAy;
+  short borderHighAy;
+  short borderLowAx;
+  short borderHighAx;
+
+  byte  lowNote;
+  byte  highNote;
+
+} Value2Midi, *Value2MidiPtr;
+
+enum Meas2Midi
+{
+  NoteType,
+  NoteVal,
+  NoteVel
+};
+
+typedef struct _MidiBorders
+{
+  byte  low;
+  byte  high;
+} MidiBorders, *MiniBordersPtr;
+
+typedef struct _Posture2Midi
+{
+  float       offsetRoll;
+  float       borderLowRoll;
+  float       borderHighRoll;
+  float       koeffRoll;
+  float       offsetPitch;
+  float       borderLowPitch;
+  float       borderHighPitch;
+  float       koeffPitch;
+  float       offsetYaw;
+  float       borderLowYaw;
+  float       borderHighYaw;
+  float       koeffYaw;
+  Meas2Midi   aimRoll;
+  Meas2Midi   aimPitch;
+  Meas2Midi   aimYaw;
+  byte        signAreaValue[4];
+  byte        signAreaCtrl[4];
+  MidiBorders midiBords[3];
+} Posture2Midi, *Posture2MidiPtr;
+
+// ----------------------------------------------------------------------------
+// Vorwärtsreferenzen
+// ----------------------------------------------------------------------------
+//
+void apInit();
+void apWaitDE();
+void apWaitMeas();
+void apProcMeas();
+void apCheckValues();
+void apCalcResult();
+void apSetResult();
+void apTestController();
+
+void setParM1();
+void setParM2();
+void setParP1();
+void setParP2();
+
+
+#ifdef DebugTerminal
+// ----------------------
+void smInit() ;
+void smCheckJobs() ;
+void smDebDword() ;
+void smCtrlPolling() ;
+void smWaitPolling() ;
+void smReadPollValues() ;
+void smCheckApp();
+void smMode4Slave();
+void smMode4SlaveIn();
+// ----------------------
+#endif
+
+#endif SoaapBleMidiMaster_h
diff --git a/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/src/main.cpp b/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/src/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b2577ce24c6307e36242ce5ce34e0b78f618d7e9
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/src/main.cpp
@@ -0,0 +1,1184 @@
+// ----------------------------------------------------------------------------
+//                              SoaapBleMidiMaster.ino
+// Beispielhafte Anwendung SOAAP / Steuerung optischer und akustischer Ausgaben
+//      Kommunikation über BLE-Funkanäle mit Bewerbungstelegrammen
+//                        P o l l i n g - M a s t e r
+// ----------------------------------------------------------------------------
+// Editor:  Robert Patzke
+// URI/URL: www.hs-hannover.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   26. April 2022
+// Letzte Bearbeitung:
+//
+
+#include  "Arduino.h"
+#include  "SoaapBleMidiMaster.h"
+
+// ----------------------------------------------------------------------------
+LoopCheck     lc;
+// ----------------------------------------------------------------------------
+// Eine statische Instanz der Klasse LoopCheck
+// Darüber wird das Zeitverhalten gesteuert (Software-Timer) und geprüft
+
+// ----------------------------------------------------------------------------
+nRF52840Radio bleCom;
+// ----------------------------------------------------------------------------
+// Eine statische Instanz der Klasse nRF52840Radio
+// Darüber wird direkt die CPU (nRF52840) auf dem Arduino-Board zum Senden und
+// Empfangen von BLE-Beacons angesprochen.
+
+
+#define bleCycleTime 250
+// ----------------------------------------------------------------------------
+BlePoll       blePoll((IntrfRadio *) &bleCom, micros);
+// ----------------------------------------------------------------------------
+// Eine statische Instanz der Klasse BlePoll
+// Darüber werden das Polling des Masters als auch die Antworten der Slaves
+// gesteuert. Es können Geräte entwickelt werden, die nur Master oder Slave
+// sind und solche mit Doppelfunktion, wenn kein expliziter Master
+// eingesetzt und das Netzwerk über Spontan-Master quasi dezentral
+// betrieben werden soll.
+// ----- Parameter ------------------------------------------------------------
+// <&bleCom>    Die Klasse (vom angenommenen Typ IntrfRadio), die die Daten-
+//              übertragung abwickelt. Hier wird eine Instanz von nRF52840Radio
+//              angebunden. Für andere Hardware kann eine entsprechende Klasse
+//              verwendet werden, die von IntrfRadio abgeleitet wurde.
+// <micros>     Eine Funktion, die die verstrichene Zeit in Mikrosekunden gibt.
+//              Damit werden Zeiten (z.B. Time-Out) berechnet.
+//              Wird hier der Wert NULL übergeben, dann werden die Zeiten aus
+//              dem Aufrufzyklus (bleCycleTime) in Mikrosekunden berechnet,
+//              was hier einer Auflösung von 250 Mikrosekunden entspricht.
+
+#define NrOfSlavesToPoll  5
+// Die Anzahl der Slaves, die der Master aufrufen soll (Polling).
+// Es wird grundsätzlich mit der Adresse 1 begonnen und nach dem Aufruf die
+// Adresse inkrementiert, also immer die Slaves Adr = 1 bis
+// Adr = NrOfSlavesToPoll+1 aufgerufen.
+// ACHTUNG!
+// Diese Zahl muss kleiner/gleich der in BlePoll.h definierten maximalen
+// Anzahl der Slaves (MAXSLAVE) sein, weil die Ressourcen darüber statisch
+// festgelegt werden.
+
+Value2Midi    val2midArr[NrOfSlavesToPoll + 1];
+Posture2Midi  post2midArr[NrOfSlavesToPoll + 1];
+// Jeder Slave bekommt spezifische Value->Midi-Parameter.
+// Index 0 steht für eventuelle Parameter des Masters
+
+SerParams     ttyParams;
+// ----------------------------------------------------------------------------
+nRF52840Ser   tty;
+// ----------------------------------------------------------------------------
+// Eine statische Instanz der Klasse nRF52840Ser (UART)
+// Darüber werden die seriellen Schnittstellen (UARTE0 und UARTE1) des
+// nRF52840 bedient.
+// Die Parameter werden in einer Struktur <SerParams> über die Funktion
+// <begin(...)> gesetzt.
+
+#define       sndBufSize  256
+byte          sndBuffer[sndBufSize];
+// ----------------------------------------------------------------------------
+ComRingBuf    crb;
+// ----------------------------------------------------------------------------
+// Eine statische Instanz der Klasse <ComRingBuf>
+// Damit wird ein Ringpuffer aufgebaut, der eine serielle Schnittstelle bedient.
+// Der Speicher muss extra eingerichtet und mit der Funktion
+//  <setWriteBuffer(sndBufSize, sndBuffer)> übergeben werden.
+// Die Klasse für die serielle Schnittstelle muss von <IntrfSerial> abgeleitet
+// sein, die Instanz wird mit der Funktion <begin(...)> übergeben.
+
+#define MidiCycle     1000
+MidiNotes  midi1,midi2;
+
+#define appCycleTime 1000
+StateMachine  ap(apInit, NULL, appCycleTime);
+// Eine statische Instanz für die Zustandsmaschine, die hier für die
+// Anwendung (App) von SOAAP eingesetzt wird
+// ----- Parameter ------------------------------------------------------------
+// <smInit>       Der zuerst aufgerufene Zustand (Funktion). Weitere Zustände
+//                werden in den weiteren Zustandsfunktionen eingesetzt.
+// <NULL>         Hier kann eine weitere Zustandsfunktion angegeben werden,
+//                die dann grundsätzlich vor dem Verzweigen in einen Zustand
+//                aufgerufen wird.
+// <smCycleTime>  Die Zukluszeit (Takt) der Zustandsmaschine in Mikrosekunden
+
+
+#ifdef DebugTerminal
+// ----------------------------------------------------------------------------
+// Zum Debuggen und weitere Analysen der Programmumgebung und Funktionstests
+// Es ist ein (richtiges) Terminal erforderlich, mit dem einzelnen Zeichen
+// direkt abgeschickt und die eintreffenden direkt angezeigt werden.
+// ----------------------------------------------------------------------------
+#define smCycleTime 5
+StateMachine  sm(smInit, NULL, smCycleTime);
+// Eine statische Instanz für die Zustandsmaschine, die hier für allgemeine
+// Steuerungen, Überwachungen und zum Debugging verwendet wird
+// ----- Parameter ------------------------------------------------------------
+// <smInit>       Der zuerst aufgerufene Zustand (Funktion). Weitere Zustände
+//                werden in den weiteren Zustandsfunktionen eingesetzt.
+// <NULL>         Hier kann eine weitere Zustandsfunktion angegeben werden,
+//                die dann grundsätzlich vor dem Verzweigen in einen Zustand
+//                aufgerufen wird.
+// <smCycleTime>  Die Zukluszeit (Takt) der Zustandsmaschine in Millisekunden
+
+char *infoThis =
+{
+"SOAAP BLE Master Midi Version 22.09.24-1\r\n"
+"c0  Abschalten der periodischen Meldung\r\n"
+"c1  Auslesen BlePoll-Debug-Register\r\n"
+"c2  Steuern/Analysieren des Polling\r\n"
+"c3  Werte auslesen\r\n"
+"c4  Anwendung analysieren\r\n"
+"c5  Meldung an Slave senden\r\n"
+};
+
+
+Monitor       mon(modeEcho | modeNl,0,&lc);
+// Eine statische Instanz (mit Konstruktordaten) der Klasse Monitor
+// Darüber wird mit (direkten) Terminals (z.B. VT100) kommuniziert
+// Unter Linux werden hier GtkTerm (siehe Internet) und
+// ArduinoMonTerm (eigene Entwicklung mit grafischen Wertanzeigen) eingesetzt.
+// Das in den IDEs integrierte Terminal ist dafür meistens nicht geeigent,
+// weil damit keine direkte Kommunikation (getipptes Zeichen sofort gesendet)
+// möglich ist.
+// ----- Parameter ------------------------------------------------------------
+// <mode Echo>  Alle eintreffenden Zeichen werden sofort zurückgesendet
+// <mode NL>    Vor der Ausgabe des Prompt (M>) erfolgt CR/LF
+// <0>          Für Speicherzugriffe wird von 32 Bit ARM ausgegangen
+// <&lc>        Für Zeitüberwachungen und entsprechende statisctische Daten
+//              greift die Monitor-Klasse auf die LoopCheck-Klasse zu
+// ----------------------------------------------------------------------------
+#endif
+
+
+BlePoll::AppType  appType;
+
+// ============================================================================
+void setup()
+// ============================================================================
+{
+  bleCom.begin();           // Initialisierung der Datenübertragung
+  //bleCom.setPower(0x08);     // Maximale Sendeleistung bei nRF52840
+  // TEST
+  bleCom.setPower(0x0FC);   // Reduzierte Sendeleistung beim Schreibtisch-Test
+
+  //appType = BlePoll::atDevSOAAP;
+  appType = BlePoll::atSOAAP2;
+
+  blePoll.begin(BlePoll::ctMASTER, NrOfSlavesToPoll, appType, 10000);
+  // Initialisierung des Polling mit folgenden Parametern:
+  // <BlePoll::ctMASTER>    Es wird ein Master eingerichtet
+  // <NrOfSlavesToPoll>     Anzahl gepollter Slaves (s.o.)
+  // <BlePoll::atSOAAP>     Spezielle Anwendung SOAAP
+  // <10000>                INT-Watchdog-Timeout beim Lesen in Mikrosekunden
+
+  blePoll.setEmptyPollParams(2000, 500, 2000);
+  // Setzen der Parameter für das leere Polling zur Feststellung der
+  // vorhandenen Slaves und Aufbau einer Poll-Liste für den Datenaustausch
+  // ----- Parameter ----------------------------------------------------------
+  // <2000>   Anzahl der Poll-Durchläufe, solange kein Slave gefunden wird
+  // <500>    Anzahl weiterer Durchläufe, nachdem wenigstens ein Slave gefunden ist
+  // <2000>   Time-Out (Zeit für Slave zum Antworten) in Mikrosekunden
+
+  for(int i = 1; i <= NrOfSlavesToPoll; i++)
+    blePoll.setDataPollParams(i, 1, 10, 1000);
+  // Setzen der Parameter beim Datenpolling, für Slaves individuell
+  // ----- Parameter ----------------------------------------------------------
+  // <i>      Adresse des Slave (1-..)
+  // <1>      Priorität beim Aufruf, 0 = immer bis max 65535 = sehr selten
+  // <10>     minimale Priorität bei automatischer Prioritätsreduzierung
+  //          im Fall von Störungen (Time-Out)
+  // <1000>   Time-Out (Zeit für Slave zum Antworten) in Mikrosekunden
+
+#ifdef DebugTerminal
+  blePoll.stopEP();       // Das Polling muss extra gestartet werden
+  mon.setInfo(infoThis);  // Info-Meldung für Monitor
+#endif
+
+  // Initialisierung von serieller Schnittstelle und Ringpuffer
+  // --------------------------------------------------------------------------
+  ttyParams.inst    = 0;    // Instanzindex der Schnittstelle (0,1)
+  ttyParams.rxdPort = 1;    // Nummer des IO-Port mit RxD-Pin
+  ttyParams.rxdPin  = 10;   // Nummer des RxD-Pin am Port
+  ttyParams.txdPort = 1;    // Nummer des IO-Port mit TxD-Pin
+  ttyParams.txdPin  = 3;    // Nummer des TxD-Pin am Port
+  ttyParams.speed   = Baud31250;    // Enumerator für Bitrate
+  ttyParams.type    = stCur;        // Stromschleife
+
+  tty.begin(&ttyParams, (IntrfBuf *) &crb);
+  // Übergeben von Parametern und Referenz auf Ringpufferverwaltung
+  // für die Übergabe empfangener Zeichen
+
+  tty.startSend();    // Sendebetrieb aktivieren
+
+  crb.setWriteBuffer(sndBufSize, sndBuffer);
+  // Speicher an Ringpufferverwaltung übergeben
+
+  crb.begin((IntrfSerial *) &tty);
+  // Referenz auf Schnittstelle an Ringpufferverwaltung
+  // für die Übergabe zu sendender Zeichen
+
+  setParM1();
+  setParP1();
+  midi1.begin(120, nd32, MidiCycle, (IntrfBuf *) &crb);
+  midi1.setChannel(1);
+
+  setParM2();
+  setParP2();
+  midi2.begin(120, nd32, MidiCycle, (IntrfBuf *) &crb);
+  midi2.setChannel(2);
+  //midi2.stop();
+}
+
+// ============================================================================
+void loop()
+// ============================================================================
+{
+  lc.begin();     // Muss am Anfang von LOOP aufgerufen werden
+  // --------------------------------------------------------------------------
+
+#ifdef DebugTerminal
+  mon.run();      // Der Monitor bekommt bei jedem Durchlauf die CPU
+#endif
+
+  // Alle 250 Mikrosekunden erfolgt der Aufruf des Ble-Polling
+  //
+  if(lc.timerMicro(lcTimer0, bleCycleTime, 0))
+    blePoll.run();
+  // ----- Parameter der Software-Timer ---------------------------------------
+  // <lcTimer0>   Id/Nummer des Timer, zur Zeit werden bis 10 Timer unterstützt
+  //              lcTimer0 bis lcTimer9 (einstellbar in LoopCheck.h)
+  // <bleCycleTime>   Ablaufzeit in Einheit des Timer-Typ (Micro/Milli)
+  //                  hier in Mikrosekunden (timerMicro)
+  // <0>          Anzahl der Wiederholungen, 0 = unbegrenzt
+
+  // Alle <appCycleTime> Mikrosekunden erfolgt der Aufruf der Anwendung
+  //
+  if(lc.timerMicro(lcTimer1, appCycleTime, 0, 10000))
+    ap.run();
+
+  // Alle <MidiCycle> Mikrosekunden erfolgt der Aufruf des Midi-Controller
+  //
+  if(lc.timerMicro(lcTimer2, MidiCycle, 0, 1000))
+  {
+    midi1.run();
+    midi2.run();
+  }
+
+#ifdef DebugTerminal
+  // Jede Sekunde erfolgt die Ausgabe einer Versionsmeldung
+  // das kann über c0 am Terminal abgeschaltet werden
+  //
+  if(lc.timerMilli(lcTimer3, 1000, 0))
+  {
+    if(!mon.cFlag[0])
+      mon.printcr((char *)"%@TestBleMidiMaster, Version 20221018");
+  }
+  // Die Zeichen %@ am Anfang steuern die Ausgabe bei AndroidMonTerm in ein
+  // Textfeld (Label) statt auf das Terminal-Display
+
+  // Jede Millisekunde erfolgt der Aufruf der Zustandsmaschine
+  //
+  if(lc.timerMilli(lcTimer4, smCycleTime, 0))
+  {
+    sm.run();
+  }
+#endif
+
+  // --------------------------------------------------------------------------
+  lc.end();       // Muss vor dem Ende von LOOP aufgerufen werden
+}
+
+// ****************************************************************************
+// Z u s t a n d s m a s c h i n e   S O A A P - A n w e n d u n g  (ap)
+// ****************************************************************************
+//
+byte  apTmpByteArray[256];              // Zwischenspeicher für Zeichenfolgen
+int   apNrOfMeasBytes;                  // Anzahl der empfangenen Messwertbytes
+byte  apMeasByteArray[32];              // Zwischenspeicher für Messwerte
+byte  apSlaveList[NrOfSlavesToPoll];    // Merker für gepollte Slaves
+int   apNrOfSlaves;                     // Aktuelle Anzahl von Slaves
+int   curListIdx;                       // Aktueller Index für Slaveliste
+int   area;                             // Area des aktuellen Slave
+int   sMsgLen;                          // Länge einer SOAAP-Meldung
+int   slNr;                             // Slave-Nummer (Adresse)
+int   txNr;                             // Anzahl versendeter Zeichen
+
+PlpType   pAppId;                       // Anwendungs-Id aus Polling-Sicht
+
+int   lastNoteIdxM1 = 0;
+int   lastNoteIdxM2 = 0;
+
+#ifdef TEST001
+char testMsgBuf[256];
+#endif
+// ----------------------------------------------------------------------------
+// Initialisierungen
+//
+dword apInitCnt;
+void apInit()
+{
+  apInitCnt++;
+
+  midi1.setNoteType(MidiNotes::nti8);
+  lastNoteIdxM1 = midi1.addChordNote(MidiNotes::nti8, SchlossC, 10);
+  midi1.setOpMode(momSequence);
+  midi2.setNoteType(MidiNotes::nti8);
+  lastNoteIdxM2 = midi2.addChordNote(MidiNotes::nti8, Kammerton, 10);
+  midi2.setOpMode(momSequence);
+  ap.enter(apWaitDE);
+}
+
+// ----------------------------------------------------------------------------
+// Warten, bis Datenaustausch Master/Slave erfolgt
+//
+dword apWaitDECnt;
+void apWaitDE()
+{
+  apWaitDECnt++;
+
+  if(!blePoll.DataExchange)
+    return;   // Verbleiben in diesem Zustand bis Leerpolling beendet
+
+  apNrOfSlaves = blePoll.getSlaveList(apSlaveList, NrOfSlavesToPoll);
+  // Ermitteln der angeschlossenen Slaves
+
+  ap.enter(apWaitMeas);
+}
+
+// ----------------------------------------------------------------------------
+// Warten auf neuen Messwert von einem Slave
+//
+dword apWaitMeasCnt;
+void apWaitMeas()
+{
+  apWaitMeasCnt++;
+
+  // Ermitteln, ob einer der Slaves einen Messwert hat
+  //
+  for(curListIdx = 0; curListIdx < apNrOfSlaves; curListIdx++)
+  {
+    slNr = apSlaveList[curListIdx];
+    if(blePoll.measAvail(slNr)) break;
+  }
+  if(curListIdx == apNrOfSlaves) return;
+  // Wenn kein Slave neue Messwerte hat,
+  // dann im nächsten Zustandstakt Abfrage wiederholen
+
+  // Slave (curListIdx) hat Messwerte übermittelt
+  // diese werden mit dem nächsten Takt verarbeitet
+  ap.enter(apProcMeas);
+}
+
+// ----------------------------------------------------------------------------
+// Verarbeiten der Daten vom Slave
+//
+dword apProcMeasCnt;
+void apProcMeas()
+{
+  apProcMeasCnt++;
+
+  // Parameter und Daten für die SOAAP-Ausgabe holen
+  //
+  slNr = apSlaveList[curListIdx];
+  area = blePoll.getArea(slNr);
+  pAppId = blePoll.getAppId(slNr);
+  apNrOfMeasBytes = blePoll.getMeas(slNr, apMeasByteArray);
+
+  // Spezifisch je Slave auswerten
+  //
+  if(slNr == 1 || slNr == 2)
+  {
+    ap.enter(apCheckValues);
+    return;
+  }
+
+  // Auf nächsten Messwert von einem Slave warten
+  //
+  ap.enter(apWaitMeas);
+}
+
+// ----------------------------------------------------------------------------
+// Daten überprüfen (auswerten)
+//
+short accXold, accYold, accZold;
+float rollOld, pitchOld, yawOld;
+dword apCheckValuesCnt;
+void apCheckValues()
+{
+  apCheckValuesCnt++;
+
+  switch (appType)
+  {
+   case BlePoll::atSOAAP1:
+     //bool newData = false;
+
+     accXold = * (short *) &apMeasByteArray[6];
+     accYold = * (short *) &apMeasByteArray[8];
+     accZold = * (short *) &apMeasByteArray[10];
+
+     break;
+
+   case BlePoll::atSOAAP2:
+     rollOld  = * (float *) &apMeasByteArray[0];
+     pitchOld = * (float *) &apMeasByteArray[4];
+     yawOld   = * (float *) &apMeasByteArray[8];
+
+     break;
+  }
+
+  ap.enter(apCalcResult);
+}
+
+// ----------------------------------------------------------------------------
+// Daten auf Midi-Noten abbilden
+//
+//short borderLowAz = 100;
+//short borderHighAz = 8000;
+//short borderLowAy = 100;
+//short borderHighAy = 8300;
+//short borderLowAx = 100;
+//short borderHighAx = 4000;
+
+//byte  lowNote = 23;
+//byte  highNote = 72;
+//byte highNote = 96;
+
+void setParM1()
+{
+  val2midArr[1].borderLowAz = 100;
+  val2midArr[1].borderHighAz = 8000;
+  val2midArr[1].borderLowAy = 100;
+  val2midArr[1].borderHighAy = 8300;
+  val2midArr[1].borderLowAx = 100;
+  val2midArr[1].borderHighAx = 4000;
+  val2midArr[1].lowNote = 23;
+  val2midArr[1].highNote = 96;
+}
+
+void setParM2()
+{
+  val2midArr[2].borderLowAz = 100;
+  val2midArr[2].borderHighAz = 8000;
+  val2midArr[2].borderLowAy = 100;
+  val2midArr[2].borderHighAy = 8300;
+  val2midArr[2].borderLowAx = 100;
+  val2midArr[2].borderHighAx = 4000;
+  val2midArr[2].lowNote = 23;
+  val2midArr[2].highNote = 96;
+}
+
+void setParP1()
+{
+  Posture2MidiPtr pmp = &post2midArr[1];
+
+  pmp->offsetRoll               = 90;
+  pmp->borderLowRoll            = 45;
+  pmp->borderHighRoll           = 170;
+  pmp->aimRoll                  = NoteType;
+
+  pmp->offsetPitch              = 90;
+  pmp->borderLowPitch           = 45;
+  pmp->borderHighPitch          = 170;
+  pmp->aimPitch                 = NoteVal;
+
+  pmp->midiBords[NoteVal].low   = 23;
+  pmp->midiBords[NoteVal].high  = 96;
+  pmp->midiBords[NoteType].low  = 2;
+  pmp->midiBords[NoteType].high = 12;
+
+  pmp->koeffRoll =
+      (pmp->midiBords[pmp->aimRoll].high - pmp->midiBords[pmp->aimRoll].low) /
+      (pmp->borderHighRoll - pmp->borderLowRoll);
+
+  pmp->koeffPitch =
+      (pmp->midiBords[pmp->aimPitch].high - pmp->midiBords[pmp->aimPitch].low) /
+      (pmp->borderHighPitch - pmp->borderLowPitch);
+}
+
+void setParP2()
+{
+  Posture2MidiPtr pmp = &post2midArr[2];
+
+  pmp->offsetRoll               = 90;
+  pmp->borderLowRoll            = 45;
+  pmp->borderHighRoll           = 170;
+  pmp->aimRoll                  = NoteType;
+
+  pmp->offsetPitch              = 90;
+  pmp->borderLowPitch           = 45;
+  pmp->borderHighPitch          = 170;
+  pmp->aimPitch                 = NoteVal;
+
+  pmp->midiBords[NoteVal].low   = 23;
+  pmp->midiBords[NoteVal].high  = 96;
+  pmp->midiBords[NoteType].low  = 2;
+  pmp->midiBords[NoteType].high = 12;
+
+  pmp->koeffRoll =
+      (pmp->midiBords[pmp->aimRoll].high - pmp->midiBords[pmp->aimRoll].low) /
+      (pmp->borderHighRoll - pmp->borderLowRoll);
+
+  pmp->koeffPitch =
+      (pmp->midiBords[pmp->aimPitch].high - pmp->midiBords[pmp->aimPitch].low) /
+      (pmp->borderHighPitch - pmp->borderLowPitch);
+}
+
+
+byte  resultAz;
+byte  resultAy;
+byte  resultAx;
+
+byte  resultNote[3];
+
+dword apCalcResultCnt;
+void apCalcResult()
+{
+  short     testY;
+  int       result;
+  float     rollVal, pitchVal, yawVal;
+  Meas2Midi meas2midi;
+
+  apCalcResultCnt++;
+
+  switch (appType)
+  {
+    case BlePoll::atSOAAP1:
+      if((accZold < val2midArr[slNr].borderLowAz) && (accXold < val2midArr[slNr].borderLowAx))
+      {
+        ap.enter(apWaitMeas);
+        return;
+      }
+
+      testY = accZold + accYold / 4;
+      result = val2midArr[slNr].lowNote
+          + (val2midArr[slNr].highNote * testY) / val2midArr[slNr].borderHighAz;
+      if(result > val2midArr[slNr].highNote)
+        resultAz = val2midArr[slNr].highNote;
+      else if (result < val2midArr[slNr].lowNote)
+        resultAz = val2midArr[slNr].lowNote;
+      else
+        resultAz = result;
+
+      testY = accXold + accYold / 4;
+      result = (MidiNotes::nti32 * testY) / val2midArr[slNr].borderHighAz;
+      if(result < MidiNotes::nti2) result = MidiNotes::nti2;
+      if(result > MidiNotes::nti32) result = MidiNotes::nti32;
+      resultAx = result;
+      break;
+
+    case BlePoll::atSOAAP2:
+      rollVal = rollOld + post2midArr[slNr].offsetRoll; // Rollwinkel in positiven Bereich verschieben
+      if(rollVal < post2midArr[slNr].borderLowRoll)
+      {
+        rollVal = post2midArr[slNr].borderLowRoll;      // Inhalt ggf. auf Minimalwert begrenzen
+      }
+      else if(rollVal > post2midArr[slNr].borderHighRoll)
+      {
+        rollVal = post2midArr[slNr].borderHighRoll;     // Inhalt ggf. auf Maximalwert begrenzen
+      }
+      meas2midi = post2midArr[slNr].aimRoll;
+      resultNote[meas2midi] =
+          post2midArr[slNr].midiBords[meas2midi].low +
+          (byte) ((rollVal - post2midArr[slNr].borderLowRoll) * post2midArr[slNr].koeffRoll);
+
+      pitchVal = pitchOld + post2midArr[slNr].offsetPitch; // Pitchwinkel in positiven Bereich verschieben
+      if(pitchVal < post2midArr[slNr].borderLowPitch)
+      {
+        pitchVal = post2midArr[slNr].borderLowPitch;    // Inhalt ggf. auf Minimalwert begrenzen
+      }
+      else if(pitchVal > post2midArr[slNr].borderHighPitch)
+      {
+        pitchVal = post2midArr[slNr].borderHighPitch;   // Inhalt ggf. auf Maximalwert begrenzen
+      }
+      meas2midi = post2midArr[slNr].aimPitch;
+      resultNote[meas2midi] =
+          post2midArr[slNr].midiBords[meas2midi].low +
+          (byte) ((pitchVal - post2midArr[slNr].borderLowPitch) * post2midArr[slNr].koeffPitch);
+      resultNote[NoteVel] = 100;
+      break;
+  }
+
+  ap.enter(apSetResult);
+}
+
+// ----------------------------------------------------------------------------
+// Bedienen des Midi-Controller
+//
+dword apSetResultCnt;
+void apSetResult()
+{
+  apSetResultCnt++;
+
+  switch (appType)
+  {
+    case BlePoll::atSOAAP1:
+      if(slNr == 1)
+        midi1.setChordNote(lastNoteIdxM1, (MidiNotes::NoteTypeIdx) resultAx, resultAz, 100);
+      else if(slNr == 2)
+        midi2.setChordNote(lastNoteIdxM2, (MidiNotes::NoteTypeIdx) resultAx, resultAz, 100);
+      break;
+
+    case BlePoll::atSOAAP2:
+      if(slNr == 1)
+        midi1.setChordNote
+        (
+          lastNoteIdxM1,
+          (MidiNotes::NoteTypeIdx) resultNote[NoteType],
+          resultNote[NoteVal],
+          resultNote[NoteVel]
+        );
+      else if(slNr == 2)
+        midi2.setChordNote
+        (
+          lastNoteIdxM1,
+          (MidiNotes::NoteTypeIdx) resultNote[NoteType],
+          resultNote[NoteVal],
+          resultNote[NoteVel]
+        );
+      break;
+  }
+
+  ap.enter(apWaitMeas);
+}
+
+byte  testValue = 22;
+
+void apTestController()
+{
+  midi1.setChordNote(lastNoteIdxM1, MidiNotes::nti4, testValue, 60);
+  testValue++;
+  if(testValue > 96)
+    testValue = 22;
+  ap.setDelay(100);
+}
+
+
+
+#ifdef DebugTerminal
+// ****************************************************************************
+// Z u s t a n d s m a s c h i n e   z u m   D e b u g g e n   (sm)
+// ****************************************************************************
+//
+dword   debDword;
+byte    tmpByteArray[256];
+
+
+void smInit()
+{
+  sm.enter(smCheckJobs);
+}
+
+// ----------------------------------------------------------------------------
+// Abfrage der Monitorschalter
+// ----------------------------------------------------------------------------
+//
+
+void smCheckJobs()
+{
+  if(mon.cFlag[1] && !mon.busy)
+    sm.enter(smDebDword);
+  else if(mon.cFlag[2] && !mon.busy)
+    sm.enter(smCtrlPolling);
+  else if(mon.cFlag[3] && !mon.busy)
+    sm.enter(smReadPollValues);
+  else if(mon.cFlag[4] && !mon.busy)
+    sm.enter(smCheckApp);
+  else if(mon.cFlag[5] && !mon.busy)
+    sm.enter(smMode4Slave);
+}
+
+// ----------------------------------------------------------------------------
+// Debug-Informationen
+// ----------------------------------------------------------------------------
+//
+
+void smDebDword()
+{
+  int idx;
+
+  if(sm.firstEnter())
+  {
+    mon.print((char *) "DebDword[");
+    mon.lastKeyIn = ':';
+  }
+
+  if(mon.lastKeyIn == ':') return;
+
+  if(mon.lastKeyIn >= 0x30 && mon.lastKeyIn <= 0x39)
+  {
+    idx = mon.lastKeyIn & 0x0F;
+    mon.print(idx);
+    mon.print((char *) "]=");
+    debDword = blePoll.debGetDword(idx);
+    mon.println(debDword);
+    sm.resetEnter();
+  }
+  else
+  {
+    if(mon.lastKeyIn == ' ')
+    {
+      mon.cFlag[1] = false;
+      mon.print((char *) "-- Schleifenabbruch - drücke Enter");
+      sm.enter(smCheckJobs);
+    }
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Steuern des Polling-Prozesses
+// ----------------------------------------------------------------------------
+//
+
+char *smPollHelp =
+{
+    "C   Pollzähler zurücksetzen\r\n"
+    "L   Liste der Slaves anzeigen\r\n"
+    "P   Polling starten/stoppen/fortsetzen\r\n"
+    "R   Radiopuffer auslesen (16 Zeichen)\r\n"
+    "S   Sendepuffer auslesen (16 Zeichen)\r\n"
+    "T   Übertragungsstatistik und Daten anzeigen\r\n"
+    "0...9  Slave-Umgebung\r\n"
+};
+
+TxStatistics txStatistics;
+
+void smCtrlPolling()
+{
+  dword   tmpDw;
+  short   tmpShort;
+  int     i;
+
+  PlpMeas6Ptr resPtr;
+
+  if(sm.firstEnter())
+  {
+    mon.print((char *) "polling ");
+    mon.lastKeyIn = ':';
+  }
+
+  if(mon.lastKeyIn == ':') return;
+
+  if(mon.lastKeyIn == 'H' || mon.lastKeyIn == 'h')
+  {
+    mon.println();
+    mon.print(smPollHelp);
+    sm.resetEnter();
+  }
+
+  // --------------------------------------------------------------------------
+  else if(mon.lastKeyIn == 'P' || mon.lastKeyIn == 'p')
+  {
+    if(blePoll.stoppedEP())
+    {
+      blePoll.resumeEP();
+      mon.println((char *) "fortgesetzt");
+      sm.resetEnter();
+    }
+    else
+    {
+    blePoll.stopEP();
+    sm.enter(smWaitPolling);
+    }
+  }
+
+  // --------------------------------------------------------------------------
+  else if(mon.lastKeyIn == 'C' || mon.lastKeyIn == 'c')
+  {
+    blePoll.resetPollCounters();
+    mon.println((char *) "Zähler zurückgesetzt");
+    sm.resetEnter();
+  }
+
+
+  // --------------------------------------------------------------------------
+  else if(mon.lastKeyIn == 'R' || mon.lastKeyIn == 'r')
+  {
+    mon.print((char *) "Radiopuffer = ");
+    bleCom.getPduMem(tmpByteArray, 0, 16);
+    mon.println(tmpByteArray, 16, ' ');
+    sm.resetEnter();
+  }
+
+
+  // --------------------------------------------------------------------------
+  else if(mon.lastKeyIn == 'S' || mon.lastKeyIn == 's')
+  {
+    mon.print((char *) "Sendepuffer = ");
+    bleCom.getPduSentS(tmpByteArray, 0, 16);
+    mon.println(tmpByteArray, 16, ' ');
+    sm.resetEnter();
+  }
+
+  // --------------------------------------------------------------------------
+  else if(mon.lastKeyIn == 'L' || mon.lastKeyIn == 'l')
+  {
+    mon.print((char *) "Slave-Liste: ");
+    int nrOfSlaves = blePoll.getSlaveList(tmpByteArray, 255);
+    mon.println(tmpByteArray, nrOfSlaves, ',');
+    sm.resetEnter();
+  }
+
+  // --------------------------------------------------------------------------
+  else if(mon.lastKeyIn == 'T' || mon.lastKeyIn == 't')
+  {
+    mon.print((char *) "TxStat [");
+    dword bleStat = blePoll.getStatistics(&txStatistics);
+    mon.print(bleStat);
+    mon.print((char *) "] ");
+    mon.print(txStatistics.mode); mon.cprint(' ');
+    mon.print(txStatistics.interrupts); mon.cprint(' ');
+    mon.print(txStatistics.recs); mon.cprint(' ');
+    mon.print(txStatistics.sendings); mon.cprint(' ');
+    mon.print(txStatistics.aliens); mon.cprint(' ');
+    mon.print(txStatistics.wrongs); mon.cprint(' ');
+    mon.print(txStatistics.pollAcks); mon.cprint(' ');
+    mon.print(txStatistics.pollNaks); mon.cprint(' ');
+    mon.print(txStatistics.crcErrors); mon.print("  s[ ");
+    mon.print(txStatistics.memDumpSnd,8,' '); mon.print("]  r[ ");
+    mon.print(txStatistics.memDumpRec,16,' '); mon.cprintln(']');
+    sm.resetEnter();
+  }
+
+  // --------------------------------------------------------------------------
+  else if(mon.lastKeyIn >= '0' && mon.lastKeyIn <= '9')
+  {
+    int idx = mon.lastKeyIn & 0x0F;
+    SlavePtr slPtr = blePoll.getSlavePtr(idx);
+    PollStatePtr pPtr = blePoll.getPollPtr(slPtr->pIdx);
+
+    mon.print((char *) "Slave[");
+    mon.print(idx);
+    mon.print((char *) "] ");
+    mon.print(slPtr->cntTo); mon.cprint(' ');
+    mon.print(slPtr->cntNakEP); mon.cprint(' ');
+    mon.print(slPtr->cntAckDP); mon.cprint(' ');
+
+    if(slPtr->cntAckDP == 0)
+    {
+      if(slPtr->cntTo == 0)
+        tmpDw = slPtr->cntNakEP;
+      else
+        tmpDw = slPtr->cntNakEP / slPtr->cntTo;
+    }
+    else
+    {
+      if(slPtr->cntTo == 0)
+        tmpDw = slPtr->cntAckDP;
+      else
+        tmpDw = slPtr->cntAckDP / slPtr->cntTo;
+    }
+    mon.print(tmpDw); mon.cprint(' ');
+
+    resPtr = (PlpMeas6Ptr) &slPtr->result;
+
+    mon.print(slPtr->cntLostPdu); mon.cprint('|');
+    mon.print(slPtr->cntErrCrc); mon.cprint('|');
+    //mon.print(slPtr->result.measCnt); mon.cprint('|');
+    mon.print(resPtr->measCnt); mon.cprint('|');
+    mon.print(slPtr->cntLostMeas); mon.cprint(' ');
+
+    for(i = 0; i < 6; i++)
+    {
+      //tmpShort = (short) slPtr->result.meas[i];
+      tmpShort = (short) resPtr->meas[i];
+            mon.prints(tmpShort); mon.cprint(' ');
+    }
+    mon.print((char *) "  Poll[");
+    mon.print(slPtr->pIdx);
+    mon.print((char *) "] ");
+    mon.print(pPtr->status); mon.cprint(' ');
+    mon.println(pPtr->slIdx);
+    sm.resetEnter();
+  }
+
+
+
+  else
+  {
+    if(mon.lastKeyIn == ' ')
+    {
+      mon.cFlag[2] = false;
+      mon.print((char *) "-- Schleifenabbruch - drücke Enter");
+      sm.enter(smCheckJobs);
+    }
+  }
+}
+
+void smWaitPolling()
+{
+  if(!blePoll.stoppedEP()) return;
+
+  mon.println((char *) "angehalten");
+  sm.enter(smCtrlPolling);
+}
+
+// ----------------------------------------------------------------------------
+// Zugriff auf die Sensordaten
+// ----------------------------------------------------------------------------
+//
+
+char *rpValHelp =
+{
+"0-9  Sensordaten\r\n"
+"Leerzeichen für Abbruch\r\n"
+};
+
+void smReadPollValues()
+{
+  PlPduMeasPtr  resultPtr;
+  PlPduMeasPtr  ctrlPtr;
+  byte          appId;
+  float         tmpFloat;
+  char          conv[32];
+
+  if(sm.firstEnter())
+  {
+    mon.print((char *) "Werte vom Sensor ");
+    mon.lastKeyIn = ':';
+  }
+
+  if(mon.lastKeyIn == ':') return;
+
+  if(mon.lastKeyIn == 'H' || mon.lastKeyIn == 'h')
+  {
+    mon.println();
+    mon.println(rpValHelp);
+    sm.resetEnter();
+  }
+  // --------------------------------------------------------------------------
+  else if(mon.lastKeyIn >= '0' && mon.lastKeyIn <= '9')
+  {
+    int idx = mon.lastKeyIn & 0x0F;
+    SlavePtr slPtr = blePoll.getSlavePtr(idx);
+    PollStatePtr pPtr = blePoll.getPollPtr(slPtr->pIdx);
+    resultPtr = (PlPduMeasPtr) &slPtr->result;
+    ctrlPtr = (PlPduMeasPtr) &slPtr->control;
+    appId = resultPtr->appId;
+
+    mon.print((char *) "Slave[");
+    mon.print(idx);
+    mon.print((char *) "] pduCnt=");
+    mon.print(resultPtr->counter);
+    mon.print((char *) " pduType=");
+    mon.print(resultPtr->type);
+    mon.print((char *) " appId=");
+    mon.print(resultPtr->appId);
+    mon.print((char *) " measCnt=");
+    mon.println(resultPtr->measCnt);
+
+    mon.println(resultPtr->plData,27,' ');
+
+    switch (appId)
+    {
+      case plptIMU3F4Ctrl4:
+        mon.print("AppId: plptIMU3F4Ctrl4");
+        mon.print(" Roll=");
+        tmpFloat = ((PlpI3S4C4Ptr) resultPtr)->meas[0];
+        sprintf(conv,"%f",tmpFloat);
+        mon.print(conv);
+        mon.print(" Pitch=");
+        tmpFloat = ((PlpI3S4C4Ptr) resultPtr)->meas[1];
+        sprintf(conv,"%f",tmpFloat);
+        mon.print(conv);
+        break;
+
+      default:
+        mon.print(" Unbekannte AppId");
+        break;
+    }
+    mon.println();
+    sm.resetEnter();
+  }
+  else
+  {
+    if(mon.lastKeyIn == ' ')
+    {
+      mon.cFlag[3] = false;
+      mon.print((char *) "-- Schleifenabbruch - drücke Enter");
+      sm.enter(smCheckJobs);
+    }
+  }
+
+}
+
+// ----------------------------------------------------------------------------
+// Analysieren der Anwendung
+// ----------------------------------------------------------------------------
+//
+char *caHelp =
+{
+"c Zyklen der Zustandsmaschine\r\n"
+"m Ausgabe Midiwerte\r\n"
+"Leerzeichen für Abbruch\r\n"
+};
+
+void smCheckApp()
+{
+  if(sm.firstEnter())
+  {
+    mon.print((char *) "Check App ");
+    mon.lastKeyIn = ':';
+  }
+
+  if(mon.lastKeyIn == ':') return;
+
+  if(mon.lastKeyIn == 'H' || mon.lastKeyIn == 'h')
+  {
+    mon.println();
+    mon.println(caHelp);
+    sm.resetEnter();
+  }
+  else if(mon.lastKeyIn == 'C' || mon.lastKeyIn == 'c')
+  {
+    mon.cprintln('c');
+    mon.print(apInitCnt); mon.cprint(' ');
+    mon.print(apWaitDECnt); mon.cprint(' ');
+    mon.print(apWaitMeasCnt); mon.cprint(' ');
+    mon.print(apProcMeasCnt); mon.cprint(' ');
+    mon.print(apCheckValuesCnt); mon.cprint(' ');
+    mon.print(apCalcResultCnt); mon.cprint(' ');
+    mon.println(apSetResultCnt);
+    sm.resetEnter();
+  }
+  else if(mon.lastKeyIn == 'M' || mon.lastKeyIn == 'm')
+  {
+    mon.cprintln('m');
+    mon.print(slNr); mon.cprint(' ');
+    mon.print(resultNote[NoteType]); mon.cprint(' ');
+    mon.print(resultNote[NoteVal]); mon.cprint(' ');
+    mon.println(resultNote[NoteVel]);
+    sm.resetEnter();
+  }
+
+  else if(mon.lastKeyIn == ' ')
+  {
+    mon.cFlag[4] = false;
+    mon.print((char *) "-- Schleifenabbruch - drücke Enter");
+    sm.enter(smCheckJobs);
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Modus (Polling-Info, Steuerung) an einen Slave
+// ----------------------------------------------------------------------------
+// ACHTUNG!
+// Diese Testfunktion ist allgemein gehalten. Tatsächlich wird zur Zeit noch
+// nicht der Datentyp ausgewertet und immer die ersten zwei Zeichen Inhalt
+// zum Slave übertragen
+//
+char *msHelp =
+{
+"1. 1..5 Slaveadresse\r\n"
+"2. 0..9 Modustyp oder A für Antwort\r\n"
+"   xyz  Modus, Senden mit TAB\r\n"
+"Leerzeichen für Abbruch\r\n"
+};
+
+int   inIdx = 0;
+int   msSlaveNr = 0;
+int   msModeType = 0;
+
+CtrlResp2 ctrlResp;
+byte      oldProcCnt = 0;
+
+void smMode4Slave()
+{
+  //CtrlData2Ptr ctrlDataPtr;
+
+  if(sm.firstEnter())
+  {
+    mon.print((char *) "Meldung an Slave[");
+    mon.lastKeyIn = ':';
+    inIdx = 0;
+  }
+
+  if(mon.lastKeyIn == ':') return;
+
+  if(mon.lastKeyIn == 'H' || mon.lastKeyIn == 'h')
+  {
+    mon.println();
+    mon.println(msHelp);
+    sm.resetEnter();
+  }
+  else if(mon.lastKeyIn >= '1' && mon.lastKeyIn <= '5' && inIdx == 0)
+  {
+    mon.cprint(mon.lastKeyIn);
+    msSlaveNr = mon.lastKeyIn & 0x0F;
+    mon.print("] Typ = ");
+    inIdx = 1;
+    mon.lastKeyIn = ':';
+  }
+  else if(mon.lastKeyIn >= '0' && mon.lastKeyIn <= '9' && inIdx == 1)
+  {
+    mon.cprint(mon.lastKeyIn);
+    msModeType = mon.lastKeyIn & 0x0F;
+    sm.enter(smMode4SlaveIn);
+  }
+  else if((mon.lastKeyIn == 'A' || mon.lastKeyIn == 'a') && inIdx == 1)
+  {
+    mon.print("Antwort: {");
+    blePoll.getCtrlResp(msSlaveNr, &ctrlResp);
+
+    if(ctrlResp.procCnt != oldProcCnt)
+    {
+      oldProcCnt = ctrlResp.procCnt;
+      mon.cprint(ctrlResp.ctrl[0]);
+      mon.cprint(ctrlResp.ctrl[1]);
+    }
+    mon.cprintln('}');
+    sm.resetEnter();
+  }
+
+  else if(mon.lastKeyIn == ' ')
+  {
+    mon.cFlag[5] = false;
+    mon.print((char *) "-- Schleifenabbruch - drücke Enter");
+    sm.enter(smCheckJobs);
+  }
+}
+
+char mode2Snd[8];
+
+void smMode4SlaveIn()
+{
+  if(sm.firstEnter())
+  {
+    mon.print((char *) " {");
+    mon.lastKeyIn = ':';
+    inIdx = 0;
+  }
+
+  if(mon.lastKeyIn == ':') return;
+
+  if(mon.lastKeyIn == ' ')
+  {
+    mon.cFlag[5] = false;
+    mon.print((char *) "-- Schleifenabbruch - drücke Enter");
+    sm.enter(smCheckJobs);
+  }
+
+  else if(mon.lastKeyIn == '\t' || inIdx > 5)
+  {
+    blePoll.updControl(msSlaveNr, (byte *) mode2Snd, 2);
+    mon.println("} gesendet");
+    sm.enter(smMode4Slave);
+  }
+
+  else
+  {
+    mode2Snd[inIdx] = mon.lastKeyIn;
+    mon.cprint(mon.lastKeyIn);
+    mon.lastKeyIn = ':';
+    inIdx++;
+  }
+
+}
+
+// ----------------------------------------------------------------------------
+// Debug-Informationen
+// ----------------------------------------------------------------------------
+//
+
+#endif // DebugTerminal
+
diff --git a/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/test/README b/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/test/README
new file mode 100644
index 0000000000000000000000000000000000000000..9b1e87bc67c90e7f09a92a3e855444b085c655a6
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/22_11_11_1_BLE_Master_Soaap/test/README
@@ -0,0 +1,11 @@
+
+This directory is intended for PlatformIO Test Runner and project tests.
+
+Unit Testing is a software testing method by which individual units of
+source code, sets of one or more MCU program modules together with associated
+control data, usage procedures, and operating procedures, are tested to
+determine whether they are fit for use. Unit testing finds problems early
+in the development cycle.
+
+More information about PlatformIO Unit Testing:
+- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html