diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/BlePoll/BlePoll.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/BlePoll/BlePoll.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f2b89ae3d67d527cf753ec12a685ac02e0bde3ae
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/BlePoll/BlePoll.cpp
@@ -0,0 +1,1624 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   BlePoll.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   1. September 2021
+//
+// Diese Bibliothek (Klassse) enthält diverse Ressourcen zur Kommunikation
+// über BLE-Funkmodule auf niedriger Ebene, also dem direkten Telegrammaustausch.
+// Darauf aufbauend sollen mehrkanalige Messeinrichtungen mit möglichst
+// geringen Latenzzeiten entwickelt werden.
+//
+
+#include "BlePoll.h"
+
+// --------------------------------------------------------------------------
+// Textmakros zur Vereinfachung der Programmierung
+// --------------------------------------------------------------------------
+
+#define next(x) nextState = &BlePoll::x
+
+// --------------------------------------------------------------------------
+// Initialisierungen
+// --------------------------------------------------------------------------
+
+BlePoll::BlePoll(IntrfRadio *refRadio, dword inCycleMics)
+{
+  init(refRadio, inCycleMics, NULL);
+}
+
+BlePoll::BlePoll(IntrfRadio *refRadio, MicsecFuPtr inMicroFu)
+{
+  init(refRadio, 0, inMicroFu);
+}
+
+void BlePoll::init(IntrfRadio *refRadio, dword inCycleMics, MicsecFuPtr inMicroFu)
+{
+  radio = refRadio;
+  radio->setPacketParms(bptAdv);
+  radio->setAccessAddress(AdvAccAddr);
+  chn = adr = area = 0;
+  master = nak = eadr = false;
+  plMode = plmIdle;
+  plpType = plptEmpty;
+  micSec = inMicroFu;
+  cycleMics = inCycleMics;
+  cycleCnt = 0;
+  toValue = 0;
+  toSet = 0;
+  nextState = NULL;
+  slaveIdx = 0;
+  pollIdx = 0;
+  pollNr = 0;
+  maxAdr = MAXSLAVE;
+  curSlave = NULL;
+  cntAlien = 0;
+  cntWrong = 0;
+  cntAllNaks = 0;
+  cntWaitDisabled = 0;
+  cntPolling = 0;
+  cntAllRecs = 0;
+  cntAllTo = 0;
+  pollStopped = false;
+  pollStop = false;
+  recStopped = false;
+  recStop = false;
+  runCounter = 0;
+  newValue = false;
+  cbData = NULL;
+
+  for(int i = 1; i <= MAXSLAVE; i++)
+  {
+    slaveList[i].cntNakEP = 0;
+    slaveList[i].cntTo = 0;
+    slaveList[i].pIdx = 0;
+    slaveList[i].delayCnt = 5;
+    slaveList[i].newPdu = false;
+    slaveList[i].newMeas = false;
+    pollList[i].prioCnt = 0;
+    pollList[i].slIdx = 0;
+    pollList[i].status = 0;
+  }
+
+  DataExchange = false;
+}
+
+// ----------------------------------------------------------------------------
+void BlePoll::begin(ComType typeIn, int adrIn, AppType appType, dword watchDog)
+{
+  // TODO
+  // --------------------------------------------------------------------------
+  // Das muss nochmal völlig neu überarbeitet werden.
+  // Zur Zeit sind viele Redundanzen und teilweise Mehrdeutigkeiten enthalten,
+  // weil für jede Testanwendung spezifische Vorbereitungen gemacht wurden.
+  // --------------------------------------------------------------------------
+  //
+  wdTimeOut = watchDog;   // WatchDog-Time-Out in Mikrosekunden
+
+  if(typeIn == ctMASTER)
+    master  = true;
+  else
+    master = false;  void  resetPollCounters();
+
+
+  chn     = 0;          // 1. Bewerbungskanal
+  area    = 0;          // Default-Anwendungsbereich
+  eadr    = true;       // Start mit leerem Polling
+
+  // --------------------------------------------------------------------------
+  plMode  = plmEmpty;   // Leeres Polling (Adressenliste)
+  // --------------------------------------------------------------------------
+
+  if(master)
+  {
+    nak = true;         // Nak-Bit vom Master forciert leere Antwort
+    maxAdr = adrIn;
+    if(maxAdr > MAXSLAVE)
+      maxAdr = MAXSLAVE;
+    adr  = 1;
+    slaveIdx = adr;     // Reserve für getrennte Verwaltung von adr und slaveIdx
+    next(smInit);
+  }
+  else
+  {
+    nak = true;
+    adr = adrIn;
+    next(smInit);
+  }
+
+  if(appType == atSOAAP || appType == atTestSend || appType == atDevSOAAP)
+  {
+    pduOut.adr5 = 0x53;
+    pduOut.adr4 = 0x4F;
+    pduOut.adr3 = 0x41;
+
+    pduIn.adr5 = 0x53;
+    pduIn.adr4 = 0x4F;
+    pduIn.adr3 = 0x41;
+  }
+
+  if(appType == atTestSend)
+    plMode = plmTest;
+
+  pduOut.head = HeadS0B;
+  pduIn.head = HeadS0B;
+
+  pduOut.data[0] = 0;         // Pdu-Zähler (CNT)
+  pduIn.data[0] = 0;
+
+  pduOut.data[1] = appType;   // Pdu-Typ (TYPE)
+  pduIn.data[1] = appType;
+
+  if(appType == atSOAAP)
+  {
+    if(master)
+    {
+      valuePdu.appId = plptMeas9Ctrl4;
+      ctrlPdu.appId = plptCtrlX;
+      plMode = plmSoaapM;
+      fullCycle = true;
+    }
+    else
+    {
+      valuePdu.appId = plptMeas9Ctrl4;
+      ctrlPdu.appId = plptCtrlX;
+      plMode = plmSoaapS;
+    }
+  }
+  else if (appType == atDevSOAAP)
+  {
+    if(master)
+    {
+      valuePdu.appId = plptMeas13;
+      ctrlPdu.appId = plptCtrl2;
+      plMode = plmSoaapM;
+      fullCycle = true;
+    }
+    else
+    {
+      valuePdu.appId = plptMeas13;
+      ctrlPdu.appId = plptCtrl2;
+      plMode = plmSoaapS;
+    }
+  }
+
+  valuePdu.measCnt = 0;
+  valuePdu.counter = 0;
+  valuePdu.type = appType;
+}
+
+
+// --------------------------------------------------------------------------
+// Konfiguration
+// --------------------------------------------------------------------------
+//
+void BlePoll::setPollAddress(int chnIn, int adrIn, int areaIn, bool masterIn, bool eadrIn, bool nakIn)
+{
+  chn     = chnIn;
+  adr     = adrIn;
+  area    = areaIn;
+  master  = masterIn;
+  eadr    = eadrIn;
+  nak     = nakIn;
+}
+
+void BlePoll::setPduAddress()
+{
+  setPduAddress(&pduOut);
+}
+
+void BlePoll::setPduAddress(bcPduPtr pduPtr)
+{
+  pduPtr->adr0 = (byte) adr;
+  pduPtr->adr1 = (byte) (area & 0x3F);
+  if(nak) pduPtr->adr1 |= 0x40;
+  if(eadr) pduPtr->adr1 |= 0x80;
+  pduPtr->adr2 = (byte) chn;
+  if(master) pduPtr->adr2 |= 0x80;
+}
+
+void BlePoll::setEmptyPollParams(int cycleTotal, int cycleRun, dword timeOut)
+{
+  epCycleTotal  = cycleTotal;
+  epCycleRun    = cycleRun;
+  epTimeOut     = timeOut;
+}
+
+void BlePoll::setDataPollParams(int slAdr, int prio, int minPrio, dword timeOut)
+{
+  if(slAdr > MAXSLAVE) return;
+  slaveList[slAdr].prioSet = prio;
+  slaveList[slAdr].minPrio = minPrio;
+  slaveList[slAdr].timeOut = timeOut;
+}
+
+void BlePoll::setCbDataPtr(cbDataPtr cbPtr)
+{
+  cbData = cbPtr;
+}
+
+void BlePoll::setCbCtrlPtr(cbCtrlPtr cbPtr)
+{
+  cbCtrl = cbPtr;
+}
+
+
+dword smStartComESCnt;
+bcPdu recBeacon;
+int   lenBeacon = 0;
+
+// --------------------------------------------------------------------------
+// Hilfsfunktionen
+// --------------------------------------------------------------------------
+//
+void BlePoll::setTimeOut(dword value)
+{
+  if(micSec != NULL)
+  {
+    toSet = micSec();
+    toValue = value;
+  }
+  else
+  {
+    if(cycleMics > 1)
+      cycleCnt = value / cycleMics;
+    else
+      cycleCnt = value;
+  }
+}
+
+bool BlePoll::timeOut()
+{
+  if(micSec != NULL)
+  {
+    if((micSec() - toSet) > toValue)
+      return(true);
+    else
+      return(false);
+  }
+  else
+  {
+    if(cycleCnt > 0)
+      return(false);
+    else
+      return(true);
+  }
+}
+
+bool BlePoll::getValues(bcPduPtr pduPtr, PlpType appId)
+{
+  bool  retv = false;
+
+  switch (appId)
+  {
+    case plptMeas6:
+      pduPtr->len = sizeof(PlpMeas6) + 6;
+      break;
+
+    case plptMeas9Ctrl4:
+      pduPtr->len = sizeof(PlpM9C4) + 6;
+      break;
+  }
+  pduPtr->data[0]++;      // Pdu-Counter
+  pduPtr->data[1] = valuePdu.type;
+  pduPtr->data[2] = appId;
+
+  newValue = cbData(appId, &pduPtr->data[4]);
+
+  if(newValue)
+  {
+    retv = true;
+    pduPtr->data[3]++;              // measCnt
+  }
+  return(retv);
+}
+
+
+bool BlePoll::getCtrls(bcPduPtr pduPtr, PlpType appId)
+{
+  int   ctrlLen;
+
+  if(recBeacon.len > 6)
+    ctrlLen = recBeacon.len - 6;
+  else
+    ctrlLen = 4;
+
+  newCtrl = cbCtrl((PlpType) appId, &pduPtr->data[22], &recBeacon.data[0], ctrlLen);
+
+  return(newCtrl);
+}
+
+
+// --------------------------------------------------------------------------
+// Steuerung des Polling
+// --------------------------------------------------------------------------
+//
+
+// Aktuellen Betriebszustand einstellen
+//
+void BlePoll::start(PlMode inPlMode)
+{
+  oldPlMode = plMode;
+  plMode = inPlMode;
+  pollStop = true;
+}
+
+// Anhalten des leeren Polling
+//
+void BlePoll::stopEP()
+{
+  pollStop = true;
+}
+
+// Weiterlaufen des leeren Polling
+//
+void BlePoll::resumeEP()
+{
+  pollStop    = false;
+  pollStopped = false;
+}
+
+// Abfrage, ob gestoppt
+//
+bool BlePoll::stoppedEP()
+{
+  return(pollStopped);
+}
+
+// Anhalten des Empfangs beim Slave
+//
+void BlePoll::stopSR()
+{
+  recStop = true;
+}
+
+// Weiterlaufen des Empfangs beim Slave
+//
+void BlePoll::resumeSR()
+{
+  recStop     = false;
+  recStopped  = false;
+}
+
+// Abfrage, ob Slaveempfang gestoppt
+//
+bool BlePoll::stoppedSR()
+{
+  return(recStopped);
+}
+
+
+// Eintritt in die Zustandsmaschine
+//
+void BlePoll::run()
+{
+  runCounter++;
+  if(cycleCnt > 0) cycleCnt--;
+
+  if(nextState != NULL)
+    (this->*nextState)();
+}
+
+// --------------------------------------------------------------------------
+// Zugriff auf Polling-Informationen
+// --------------------------------------------------------------------------
+//
+int BlePoll::getSlaveList(byte *dest, int maxByte)
+{
+  int           slIdx;
+
+  for(int i = 1; i <= pollMaxNr; i++)
+  {
+    if(i == maxByte) break;
+    slIdx = pollList[i].slIdx;
+    dest[i-1] = slaveList[slIdx].adr;
+  }
+  return(pollMaxNr);
+}
+
+void  BlePoll::resetPollCounters()
+{
+  int           slIdx;
+  SlavePtr      slPtr;
+
+  for(int i = 1; i <= pollMaxNr; i++)
+  {
+    slIdx = pollList[i].slIdx;
+    slPtr = &slaveList[slIdx];
+    slPtr->cntAckDP = 0;
+    slPtr->cntErrCrc = 0;
+    slPtr->cntLostMeas = 0;
+    slPtr->cntLostPdu = 0;
+    slPtr->cntNakEP = 0;
+    slPtr->cntTo = 0;
+  }
+}
+
+
+
+
+// ****************************************************************************
+// Zustandsmaschine
+// ****************************************************************************
+//
+
+// ----------------------------------------------------------------------------
+// Verzweigung nach Anwendung (nach Anlauf)
+// ----------------------------------------------------------------------------
+//
+dword smInitCnt;
+
+void BlePoll::smInit()
+{
+  bleState = 100;
+  smInitCnt++;
+
+  switch(plMode)
+  {
+    case plmIdle:
+      break;
+
+    case plmTest:
+      next(smStartTest);
+      break;
+
+    case plmEmpty:
+      epCycleTotal = -1;
+      epCycleRun = -1;
+      next(smStartEP);
+      break;
+
+    case plmScan:
+      break;
+
+    case plmSoaapM:
+      if(pollStop)
+        pollStopped = true;
+      if(!pollStopped)
+        next(smStartEP);
+      break;
+
+    case plmSoaapS:
+      next(smStartComES);
+      break;
+
+    case plmXchg:
+      break;
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Verzweigung nach Anwendung (im Betrieb)
+// ----------------------------------------------------------------------------
+//
+dword smIdleCnt;
+
+void BlePoll::smIdle()
+{
+  bleState = 200;
+  smIdleCnt++;
+
+  switch(plMode)
+  {
+    case plmIdle:
+      break;
+
+    case plmTest:
+      next(smStartTest);
+      break;
+
+    case plmEmpty:
+      next(smStartEP);
+      break;
+
+    case plmScan:
+      if(master)
+        next(smReqComS);
+      break;
+
+    case plmXchg:
+      if(!master)
+        next(smStartComES);
+      break;
+
+    case plmSoaapM:
+      if(!pollStopped)
+        next(smStartEP);
+      break;
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Low Level Tests
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smStartTest()
+{
+  bleState = 500;
+
+  if(master)
+  {
+    nak = false;
+    adr = 1;
+    slaveIdx = adr;
+    setTimeOut(500000);
+  }
+  else
+  {
+    nak = true;
+  }
+  pduOut.len  = 6;
+  setPduAddress();
+  radio->setChannel(chn);
+  next(smWaitTest);
+}
+
+void BlePoll::smWaitTest()
+{
+  if(!timeOut()) return;
+  radio->disable(txmRead);
+  next(smLoopTest);
+}
+
+void BlePoll::smLoopTest()
+{
+  pduOut.adr0++;
+  radio->send(&pduOut, txmRead);
+  setTimeOut(500000);
+  next(smWaitTest);
+}
+
+
+
+// ----------------------------------------------------------------------------
+// L e e r e s   P o l l i n g
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smStartEP()
+{
+  bleState = 1000;
+
+  pduOut.len  = 6;          // Nur Adresse
+  radio->setChannel(chn);
+  pollMaxNr = 0;
+
+  nak = true;
+
+  if(master)
+  {
+    adr = 1;
+    slaveIdx = adr;         // Slave-Array[0] reserviert
+    pollIdx = 1;            // Poll-Array[0] reserviert
+    pollNr = 0;             // Anzahl in der Poll-Liste
+    next(smReqEadr);
+  }
+  else
+  {
+    next(smWaitEadr);
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Leeres Polling           M a s t e r
+// ----------------------------------------------------------------------------
+//
+
+void BlePoll::smReqEadr()
+{
+  bleState = 1100;
+
+  // Datenstruktur für den Slave definieren
+  //
+  curSlave = &slaveList[slaveIdx];    // Zeiger auf zu pollenden Slave
+  curSlave->adr  = adr;
+  curSlave->area = area;
+  curSlave->chn  = chn;
+
+  curPoll = &pollList[pollIdx];       // Zeiger auf freien Platz in Poll-Liste
+
+  setPduAddress();
+  setTimeOut(epTimeOut);
+
+  radio->getStatistics(&statistic);
+
+  radio->send(&pduOut, txmPoll);
+  cntPolling++;
+  next(smWaitNak);
+}
+
+
+void BlePoll::smWaitNak()
+{
+  bleState = 1110;
+
+  if(timeOut())
+  {
+    // Für die Adresse ist kein Teilnehmer vorhanden, oder die Übertragung ist gestört
+    //
+    radio->disable(txmPoll);
+
+    if(curSlave->pIdx != 0)
+    {
+      // Wenn der Teilnehmer bereits in die Poll-Liste eingetragen ist
+      // dann wird darin seine temporäre Abwesenheit markiert
+      pollList[(int) curSlave->pIdx].status &= ~psSlaveIsPresent;
+    }
+
+    // Der Time-Out-Zähler macht erkenntlich, wie stark sich Störungen auswirken
+    //
+    curSlave->cntTo++;
+    cntAllTo++;
+
+    // Nächste Adresse und zur Anforderung
+    //
+    adr++;
+    slaveIdx = adr;
+    if(adr > maxAdr)
+      next(smEndEP);
+    else
+      next(smReqEadr);
+    return;
+  }
+
+
+  if(radio->fin(txmPoll, &crcError))
+  {
+    // Auf dem Kanal wurde ein Datensatz (BLE-Beacon) empfangen
+    // und es werden die ersten 8 Byte (Header, Len und Adresse) geholt
+    //
+    cntAllRecs++;
+    radio->getRecData(&pduIn, 8);
+
+    if(pduIn.adr3 != pduOut.adr3 || pduIn.adr4 != pduOut.adr4 || pduIn.adr5 != pduOut.adr5)
+    {
+      // Wenn die höherwertigen 3 Byte der Adresse nicht mit der Anwendungskodierung
+      // übereinstimmen, dann ist es ein Fremd-Beacon.
+      // Diese werden gezählt und kennzeichnen, wie stark aktiv ein anderes
+      // BLE-Netzwerk in Reichweite ist.
+      //
+      cntAlien++;
+
+      // Nächste Adresse und zur Anforderung, nicht auf Time-Out warten
+      //
+      adr++;
+      slaveIdx = adr;
+      if(adr > maxAdr)
+        next(smEndEP);
+      else
+        next(smReqEadr);
+      return;
+    }
+
+    if(pduIn.adr0 != pduOut.adr0 || (pduIn.adr1 & 0x3F) != (pduOut.adr1 & 0x3F))
+    {
+      // Wenn die Teinehmernummer oder die Gebietsnummer falsch sind, dann ist
+      // möglicherweise ein weiterer Master aktiv, der fehlerhafterweise denselben
+      // Kanal verwendet.
+      // Auch dieses Ereignis wird gezäht.
+      cntWrong++;
+
+      // Nächste Adresse und zur Anforderung, nicht auf Time-Out warten
+      //
+      adr++;
+      slaveIdx = adr;
+      if(adr > maxAdr)
+        next(smEndEP);
+      else
+        next(smReqEadr);
+      return;
+    }
+
+    // Alles korrekt, der adressierte Teilnehmer hat rechtzeitig geantwortet
+    // Radio ist abgeschaltet
+    //
+
+    if(curSlave->pIdx != 0)
+    {
+      // Wenn der Teilnehmer bereits in die Poll-Liste eingetragen ist,
+      // dann wird er darin als aktuell anwesend markiert
+      //
+      pollList[(int) curSlave->pIdx].status |= psSlaveIsPresent;
+      pollNr++;
+    }
+    else
+    {
+      // Wenn nicht, wird der aktuelle Listeneintrag definiert
+      //
+      curPoll->status |= psSlaveIsPresent | psSlaveWasPresent;
+      curPoll->slIdx = slaveIdx;  // Querverweis zur Liste möglicher Teilnehmer
+      curSlave->pIdx = pollIdx;   // Und in der Liste auch ein Verweis zur Poll-Liste
+      pollIdx++;                  // Weiter mit nächstem Listenplatz
+      pollNr++;                   // Anzahl der beantworteten Pollings
+    }
+
+    // Die Nak-Antworten werden gezählt
+    //
+    curSlave->cntNakEP++; // Teilnehmerspezifisch
+    cntAllNaks++;         // und insgesamt
+
+    // Weiter mit nächstem Teilnehmer und nächster Adresse (TN-Nummer)
+    //
+    adr++;
+    slaveIdx = adr;
+
+    // Wenn die vorgegeben Endadresse erreicht ist
+    // dann zum Ende des Polldurchgangs über alle möglichen Teilnehmer,
+    // ansonsten nächsten Teilnehmer pollen
+    //
+    if(adr > maxAdr)
+      next(smEndEP);
+    else
+      next(smReqEadr);
+  }
+}
+
+
+void BlePoll::smEndEP()
+{
+  // Nach jedem vollständigen Polldurchlauf kann das Polling
+  // abgebrochen werden.
+  //
+  if(pollStop || pollStopped)
+  {
+    pollStopped = true;
+    next(smIdle);
+    return;
+  }
+
+  // Es werden die Maximalwerte der rechtzeitigen Antworten gebildet
+  //
+  if(pollNr > pollMaxNr)
+    pollMaxNr = pollNr;
+
+  if(pollMaxNr == 0)
+  {
+    // Wenn noch kein Teilnehmer angeschlossen ist (oder Kanal total gestört)
+    //
+    if(epCycleTotal > 0)
+    {
+      // dann wird der Pollvorgang so oft wie vorgegeben wiederholt
+      //
+      epCycleTotal--;
+      pollNr = 0;
+      pollIdx = 1;
+      adr = 1;
+      slaveIdx = adr;
+      next(smReqEadr);
+      return;
+    }
+    else if(epCycleTotal == 0)
+    {
+      // und anschließend der Idle-Zustand angenommen
+      next(smIdle);
+      return;
+    }
+  }
+  else
+  {
+    // Wenn wenigstens schon ein Teilnehmer geantwortet hat
+    //
+    if(epCycleRun > 0)
+    {
+      // dann wird der Pollvorgang auch so oft wie anderweitig vorgegeben wiederholt
+      //
+      epCycleRun--;
+      pollNr = 0;
+      //pollIdx = 1; falsch, pollIdx zeigt auf nächsten freien Platz
+      adr = 1;
+      slaveIdx = adr;
+      next(smReqEadr);
+      return;
+    }
+    else if(epCycleRun == 0)
+    {
+      // und anschließend die Datenübertragung gestartet
+      // oder das Polling ganz beendet
+      //
+      if(fullCycle)
+        next(smStartCom);
+      else
+        next(smIdle);
+      return;
+    }
+  }
+
+  // Nächster Poll-Lauf, wenn epCycleXXX noch nicht abgelaufen ist
+  // oder auf einen wert kleiner 0 gestellt wurde
+  //
+  pollNr = 0;
+  //pollIdx = 1; falsch, pollIdx zeigt auf nächsten freien Platz
+  adr = 1;
+  slaveIdx = adr;
+  next(smReqEadr);
+}
+
+// ----------------------------------------------------------------------------
+// Leeres Polling           S l a v e
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smWaitEadr()
+{
+  bleState = 1200;
+
+  if(!radio->disabled(txmRespE))
+  {
+    radio->disable(txmRespE);
+    cntWaitDisabled++;
+    return;
+  }
+
+  setPduAddress();
+  radio->send(&pduOut, txmRespE);
+  next(smEvalPoll);
+}
+
+void BlePoll::smEvalPoll()
+{
+  bleState = 1210;
+
+  radio->getStatistics(&statistic);
+  if(!radio->fin(txmRespE, &crcError)) return;
+  next(smWaitEadr);
+}
+
+// ----------------------------------------------------------------------------
+// D a t e n ü b e r t r a g u n g
+// ----------------------------------------------------------------------------
+//
+
+// Vorbereitung der Datenübertragung
+//
+void BlePoll::smStartCom()
+{
+  bleState = 2000;
+
+  if(pollStop || pollStopped) // Der Start der Datenübertragung kann
+  {                           // verzögert werden
+    pollStopped = true;
+    return;
+  }
+
+  // --------------------------------------------
+  // Auswahl des Funkkanals (Frequenz)
+  // --------------------------------------------
+  //
+  radio->setChannel(chn);
+
+  // --------------------------------------------
+  // Voreinstellungen für den Master
+  // --------------------------------------------
+  //
+  if(master)
+  {
+    // Aufbau der Polling-Liste
+    //
+    for(int i = 1; i <= pollMaxNr; i++)
+    {
+      int slIdx = pollList[i].slIdx;
+      pollList[i].prioCnt = slaveList[slIdx].prioSet;
+    }
+    pollIdx = 1;
+
+    // Vorbereitung der mit dem Polling übermittelten Daten
+    //
+    pduOut.len  = 13;                 // Adresse (6) + 7 Byte Steuerung
+    ctrlPdu.counter = 0;              // Zähler im Steuertelegramm
+    ctrlPdu.appId = plptMeas9Ctrl4;   // Info für Slave zur Antwort
+
+    next(smReqComS);
+  }
+  // --------------------------------------------
+  // Voreinstellungen für den Slave
+  // --------------------------------------------
+  //
+  else
+  {
+    nak = true;
+    next(smWaitEadr);
+  }
+
+  DataExchange = true;
+}
+
+// ----------------------------------------------------------------------------
+// Datenübertragung Master          S l a v e  - >  M a s t e r
+// ----------------------------------------------------------------------------
+//
+
+// M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M
+// Polling : Anfordern von Daten beim Slave
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smReqComS()
+{
+  bleState = 2100;
+
+  if(pollStop || pollStopped) // Das Polling kann
+  {                           // angehalten werden
+    pollStopped = true;
+    return;
+  }
+
+  // Es ist von einem vorherigen Funkempfang auszugehen
+  // Die Hardware muss erst ausgeschaltet werden
+  //
+  if(!radio->disabled(txmPoll))
+  {
+    radio->disable(txmPoll);
+    return;
+  }
+
+  // Der aufzufordernde Teilnehmer wird der Poll-Liste entnommen
+  //
+  curPoll = &pollList[pollIdx];
+
+  // Ein Aufruf erfolgt nur, wenn der Prioritätszähler auf 0 steht
+  // ansonsten wird er dekrementiert und der nächste Teilnehmer
+  // im nächsten Zustandslauf ausgewählt.
+  //
+  if(curPoll->prioCnt > 0)
+  {
+    curPoll->prioCnt--;
+    pollIdx++;
+    if(pollIdx > pollMaxNr)
+      pollIdx = 1;
+    return;
+  }
+
+  // Zugriff auf den Slave aus der Poll-Liste vorbereiten
+  //
+  slaveIdx = curPoll->slIdx;
+  curSlave = &slaveList[slaveIdx];
+
+  // Slave-spezifische Parameter setzen
+  //
+  eadr = false;         // Ist true, wenn Daten nur zum Slave übertragen werden
+  nak = false;          // Ist true, wenn keine Daten übertragen werden (empty poll)
+  adr = curSlave->adr;
+  area = curSlave->area;
+  setPduAddress();
+  setTimeOut(curSlave->timeOut);
+
+
+  // Statistic-Daten einholen für evt. Auswertung
+  //
+  radio->getStatistics(&statistic);
+
+  // Aufruf des Slave starten
+  //
+  radio->send(&pduOut, txmPoll);
+
+  cntPolling++;
+  next(smWaitAckComS);
+}
+
+// M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M
+// Warten auf die Antwort vom Slave
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smWaitAckComS()
+{
+  byte    tmpByte;
+  short   tmpShort;
+
+  // Zeiger zur spezifischen Betrachtung von Empfangsdaten
+  PlPduMeasPtr resPtr;
+
+  bleState = 2110;
+
+  if(timeOut())
+  {
+    // Wenn der Slave nicht antwortet (kann auch eine Störung sein),
+    // dann wird seine Priorität heruntergesetzt (Zählwert erhöht)
+    // und der nächste Slave aus der Poll-Liste angefragt
+    //  byte      oldPduCount;
+
+    curSlave->prioSet++;
+    if(curSlave->prioSet > curSlave->minPrio)
+      curSlave->prioSet = curSlave->minPrio;
+
+    curPoll->prioCnt = curSlave->prioSet;
+
+    curSlave->cntTo++;
+    pollIdx++;
+    if(pollIdx > pollMaxNr)
+      pollIdx = 1;
+
+    radio->disable(txmPoll);
+    next(smReqComS);
+    return;
+  }
+
+  if(radio->fin(txmPoll, &crcError))
+  {
+    cntAllRecs++;
+
+    // Wenn (irgend-) ein Beacon eingegangen ist,
+    // wird die maximale (BLE-Standard) Anzahl von Bytes kopiert
+    //
+    radio->getRecData(&pduIn, 39);
+
+    if(pduIn.adr3 != pduOut.adr3 || pduIn.adr4 != pduOut.adr4 || pduIn.adr5 != pduOut.adr5)
+    {
+      // Beacons aus fremdem Netzen werden nur gezählt und es wird weiter gewartet
+      //
+      cntAlien++;
+      pollIdx++;
+      if(pollIdx > pollMaxNr)
+        pollIdx = 1;
+
+      next(smReqComS);
+      return;
+    }
+
+    if(pduIn.adr0 != pduOut.adr0 || (pduIn.adr1 & 0x3F) != (pduOut.adr1 & 0x3F))
+    {
+      // Beacons mit falscher Slaveadresse werden ebenfalls nur gezählt
+      // Hier wird später die Rundrufübertragung implementiert
+      //
+      cntWrong++;
+      pollIdx++;
+      if(pollIdx > pollMaxNr)
+        pollIdx = 1;
+
+      next(smReqComS);
+      return;
+    }
+
+    // Antwort vom richtigen Teilnehmer ist eingegangen
+    //
+
+    if(crcError)
+    {
+      // Die Daten werden bei einem CRC-Fehler verworfen.
+      // Der Fehler wird gezählt und ist ein Hinweis auf fremde
+      // Funkaktivitäten
+      //
+      curSlave->cntErrCrc++;
+      pollIdx++;
+      if(pollIdx > pollMaxNr)
+        pollIdx = 1;
+
+      next(smReqComS);
+      return;
+    }
+
+    // Die Daten werden in der Slave-Struktur abgelegt
+    //
+    resPtr = (PlPduMeasPtr) &curSlave->result;
+
+    resPtr->counter  = pduIn.data[0];
+    resPtr->type     = pduIn.data[1];
+    resPtr->appId    = pduIn.data[2];
+    resPtr->measCnt  = pduIn.data[3];
+
+    // Die Inhalte sind abhängig von der <appId>
+    //
+    switch(resPtr->appId)
+    {
+      case plptMeas9Ctrl4:
+        ((PlpM9C4Ptr) resPtr)->meas[0]  = *(word *) &pduIn.data[4];
+        ((PlpM9C4Ptr) resPtr)->meas[1]  = *(word *) &pduIn.data[6];
+        ((PlpM9C4Ptr) resPtr)->meas[2]  = *(word *) &pduIn.data[8];
+        ((PlpM9C4Ptr) resPtr)->meas[3]  = *(word *) &pduIn.data[10];
+        ((PlpM9C4Ptr) resPtr)->meas[4]  = *(word *) &pduIn.data[12];
+        ((PlpM9C4Ptr) resPtr)->meas[5]  = *(word *) &pduIn.data[14];
+        ((PlpM9C4Ptr) resPtr)->meas[6]  = *(word *) &pduIn.data[16];
+        ((PlpM9C4Ptr) resPtr)->meas[7]  = *(word *) &pduIn.data[18];
+        ((PlpM9C4Ptr) resPtr)->meas[8]  = *(word *) &pduIn.data[20];
+        ((PlpM9C4Ptr) resPtr)->ctrlPath = pduIn.data[22];
+        ((PlpM9C4Ptr) resPtr)->procCnt  = pduIn.data[23];
+        ((PlpM9C4Ptr) resPtr)->ctrl[0]  = pduIn.data[24];
+        ((PlpM9C4Ptr) resPtr)->ctrl[1]  = pduIn.data[25];
+        break;
+
+      case plptMeas6:
+        ((PlpM9C4Ptr) resPtr)->meas[0]  = *(word *) &pduIn.data[4];
+        ((PlpM9C4Ptr) resPtr)->meas[1]  = *(word *) &pduIn.data[6];
+        ((PlpM9C4Ptr) resPtr)->meas[2]  = *(word *) &pduIn.data[8];
+        ((PlpM9C4Ptr) resPtr)->meas[3]  = *(word *) &pduIn.data[10];
+        ((PlpM9C4Ptr) resPtr)->meas[4]  = *(word *) &pduIn.data[12];
+        ((PlpM9C4Ptr) resPtr)->meas[5]  = *(word *) &pduIn.data[14];
+        ((PlpM9C4Ptr) resPtr)->meas[6]  = *(word *) &pduIn.data[16];
+        break;
+    }
+
+
+    // Zählen der verlorenen Telegramme und Messwerte
+    // beginnt um <delayCnt> Pollzyklen verzögert
+    //
+    if(curSlave->delayCnt == 0)
+    {
+      tmpByte = curSlave->result.counter - curSlave->oldPduCount;
+      if(tmpByte > 1)
+        curSlave->cntLostPdu += tmpByte - 1;
+
+      tmpByte = resPtr->measCnt - curSlave->oldMeasCount;
+      if(tmpByte != 0)
+        curSlave->newMeas = true;
+      if(tmpByte > 1)
+        curSlave->cntLostMeas += tmpByte - 1;
+    }
+    else curSlave->delayCnt--;
+
+    curSlave->oldPduCount = curSlave->result.counter;
+    curSlave->oldMeasCount = resPtr->measCnt;
+
+    curSlave->newPdu  = true;
+    curSlave->cntAckDP++;
+    curPoll->prioCnt = curSlave->prioSet;
+
+    pollIdx++;
+    if(pollIdx > pollMaxNr)
+      pollIdx = 1;
+
+    next(smReqComS);
+    return;
+  }
+}
+
+void BlePoll::smEndComS()
+{
+  if(pollStop || pollStopped)
+  {
+    pollStopped = true;
+    return;
+  }
+  // Von vorne (zur Zeit, Test)
+  //
+  adr = 1;
+  slaveIdx = adr;
+  next(smReqEadr);
+}
+
+// ----------------------------------------------------------------------------
+// Datenübertragung Master         M a s t e r  - >  S l a v e
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smReqComE()
+{
+  bleState = 4100;
+
+  if(!radio->disabled(txmRead))
+  {
+    radio->disable(txmRead);
+    return;
+  }
+
+  curSlave = &slaveList[slaveIdx];
+  curSlave->adr  = adr;
+  curSlave->area = area;
+  curSlave->chn  = chn;
+
+  setPduAddress();
+  //setTimeOut(2000);
+  // Test
+  setTimeOut(1000000);
+  radio->send(&pduOut, txmRead);
+  radio->getStatistics(&statistic);
+  cntPolling++;
+  next(smWaitNak);
+}
+
+void BlePoll::smWaitAckComE()
+{
+  bleState = 4110;
+
+  if(timeOut())
+  {
+    radio->disable(txmRead);
+    curSlave->cntTo++;
+    adr++;
+    slaveIdx = adr;
+    if(adr > maxAdr)
+      next(smEndEP);
+    else
+      next(smReqEadr);
+    return;
+  }
+
+
+  if(radio->fin(txmRead, &crcError))
+  {
+    radio->getRecData(&pduIn, 8);
+
+    if(pduIn.adr3 != pduOut.adr3 || pduIn.adr4 != pduOut.adr4 || pduIn.adr5 != pduOut.adr5)
+    {
+      cntAlien++;
+      radio->cont(txmRead);
+      return;
+    }
+
+    if(pduIn.adr0 != pduOut.adr0 || (pduIn.adr1 & 0x3F) != (pduOut.adr1 & 0x3F))
+    {
+      cntWrong++;
+      radio->cont(txmRead);
+      return;
+    }
+
+    radio->disable(txmRead);
+    curSlave->cntNakEP++;
+    cntAllNaks++;
+    adr++;
+    slaveIdx = adr;
+    if(adr > maxAdr)
+      next(smEndEP);
+    else
+      next(smReqEadr);
+  }
+  radio->getStatistics(&statistic);
+}
+
+void BlePoll::smEndComE()
+{
+  if(pollStop || pollStopped)
+  {
+    pollStopped = true;
+    return;
+  }
+  // Von vorne (zur Zeit, Test)
+  //
+  adr = 1;
+  slaveIdx = adr;
+  next(smReqEadr);
+}
+
+
+// S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S
+// ----------------------------------------------------------------------------
+// Datenübertragung Slave         M a s t e r  < - >  S l a v e
+// ----------------------------------------------------------------------------
+//
+
+// S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S
+// Vorbereitungen für den Empfang (Polling durch Master)
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smStartComES()
+{
+  bool  newValues;
+  bool  newCtrl;
+  byte  lenValues;
+  byte  appId;
+
+  bleState = 1310;
+  smStartComESCnt++;
+
+  if(recStop || recStopped)
+  {
+    recStopped = true;
+    return;
+  }
+
+  // Wenn keine Daten von der Anwendung zur Verfügung gestellt werden,
+  // dann macht der Betrieb hier keinen Sinn und der Slave geht in IDLE
+  //
+  if(cbData == NULL)
+  {
+    next(smIdle);
+    return;
+  }
+
+  // Falls der Sender noch nicht ausgeschaltet ist, muss gewartet werden
+  //
+  if(!radio->disabled(txmResp))
+  {
+    radio->disable(txmResp);
+    cntWaitDisabled++;
+    return;
+  }
+
+  // Vorbereiten des erwarteten Inhalts beim Polling durch den Master
+  //
+  nak = true;
+  eadr = true;
+  setPduAddress(&pduIn);
+  pduIn.len = 6;
+
+
+  // Vorbereiten des zu sendenden Inhalts als Antwort auf das Polling
+  //
+  nak = false;
+  eadr = false;
+  setPduAddress(&pduOut);
+
+  // Eintragen der Messwerte in das Sendetelegramm
+  //
+  if(lenBeacon == 0)            // Wenn noch kein Empfangszyklus vorliegt
+    appId = valuePdu.appId;     // dann wird der voreingestellte Satz gewählt
+  else
+    appId = recBeacon.data[2];  // ansonsten der speziell angeforderte
+
+  newValues = getValues(&pduOut, (PlpType) appId);
+
+  if((appId == plptMeas9Ctrl4) && (cbCtrl != NULL))
+    getCtrls(&pduOut, (PlpType) appId);
+
+  radio->setChannel(chn);
+  radio->send(&pduIn, &pduOut, txmResp, newValues);
+
+  setTimeOut(wdTimeOut);
+  next(smWaitComES);
+}
+
+// S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S
+void BlePoll::smWaitComES()
+{
+  bleState = 1320;
+
+  radio->getStatistics(&statistic);
+  if(timeOut())
+  {
+    next(smStartComES);
+    return;
+  }
+
+  if(!radio->fin(txmResp, &crcError)) return;
+  //
+  // Übertragung beendet, Daten empfangen (polling) und versendet (response)
+  //
+  //lenBeacon = radio->getRecData(&recBeacon, txmResp, sizeof(recBeacon));
+  next(smStartComES);
+}
+
+// --------------------------------------------------------------------------
+// Anwenderfunktionen
+// --------------------------------------------------------------------------
+//
+
+// neue Steuerungsdaten für einen Slave
+
+void BlePoll::updControl(int adr, byte *ctrlList, int nr)
+{
+  if(adr <= 0) return;
+  if(adr >= MAXSLAVE) return;
+  if(nr <= 1) return;
+  if(nr > 27) return;
+
+  SlavePtr  slavePtr = &slaveList[adr];
+  PlpCtrl27Ptr ctrlPtr = (PlpCtrl27Ptr) &slavePtr->control;
+  for(int i = 0; i < nr; i++)
+    ctrlPtr->ctrl[i] = ctrlList[i];
+  ctrlPtr->ctrlCnt++;
+  slavePtr->rspOk = false;
+}
+
+// Feststellenn, ob Übertragung der Steuerungsdaten erfolgt ist
+
+bool BlePoll::ackTrans(int adr)
+{
+  if(adr <= 0) return(false);
+  if(adr >= MAXSLAVE) return(false);
+
+  SlavePtr  slavePtr = &slaveList[adr];
+  return(slavePtr->rspOk);
+}
+
+// Feststellen, ob Steuerungsdaten beim Slave verarbeitet sind
+
+bool BlePoll::ackControl(int adr)
+{
+  if(adr <= 0) return(false);
+  if(adr >= MAXSLAVE) return(false);
+
+  SlavePtr  slavePtr = &slaveList[adr];
+  PlpCtrl27Ptr ctrlPtr = (PlpCtrl27Ptr) &slavePtr->control;
+  if(ctrlPtr->ctrlCnt == slavePtr->rspCtrlCount)
+    return(true);
+  else
+    return(false);
+}
+
+// ----------------------------------------------------------------------------
+// Zugriff auf Slavedaten über die Adresse
+// ----------------------------------------------------------------------------
+//
+
+// Feststellen, ob ein Slave neue Messwerte hat
+//
+bool  BlePoll::measAvail(int slAdr)
+{
+  if(slAdr < 1) return(false);
+  if(slAdr >= MAXSLAVE) return(false);
+
+  SlavePtr  slavePtr = &slaveList[slAdr];
+
+  if(!slavePtr->newMeas)
+    return(false);
+
+  slavePtr->newMeas = false;
+  return(true);
+}
+
+// Auslesen der Netzwerk-Area
+//
+int BlePoll::getArea(int slAdr)
+{
+  if(slAdr < 1) return(false);
+  if(slAdr >= MAXSLAVE) return(false);
+
+  SlavePtr  slavePtr = &slaveList[slAdr];
+
+  return(slavePtr->area);
+}
+
+// Auslesen der AppId aus Sicht der Klasse BlePoll
+//
+PlpType BlePoll::getAppId(int slAdr)
+{
+  if(slAdr < 1) return(plptError);
+  if(slAdr >= MAXSLAVE) return(plptError);
+
+  SlavePtr  slavePtr = &slaveList[slAdr];
+
+  return((PlpType) slavePtr->result.plData[0]);
+}
+
+// Auslesen der Messwerte
+//
+int BlePoll::getMeas(int slAdr, byte *dest)
+{
+  int     anzByte;
+  PlpType appId;
+
+  if(slAdr < 1) return(false);
+  if(slAdr >= MAXSLAVE) return(false);
+
+  SlavePtr  slavePtr = &slaveList[slAdr];
+
+  appId = (PlpType) slavePtr->result.plData[0];
+
+  switch(appId)
+  {
+    case plptMeas6:
+      anzByte = 12;
+      break;
+
+    case plptMeas9:
+      anzByte = 18;
+      break;
+
+    case plptMeas9Ctrl4:
+      anzByte = 22;
+      break;
+
+    case plptMeas13:
+      anzByte = 26;
+      break;
+
+    default:
+      anzByte = 18;
+      break;
+  }
+
+  for (int i = 0; i < anzByte; i++)
+  {
+    dest[i] = slavePtr->result.plData[i+2];
+  }
+
+  return(anzByte);
+}
+
+int BlePoll::getCtrlM(int slAdr, byte *dest){
+  int     anzByte;
+  PlpType appId;
+
+  if(slAdr < 1) return(false);
+  if(slAdr >= MAXSLAVE) return(false);
+
+  
+  SlavePtr  slavePtr = &slaveList[slAdr];
+  appId = (PlpType) slavePtr->result.plData[0];
+
+  switch(appId)
+  {
+    case plptMeas6:
+      anzByte = 0;
+      break;
+
+    case plptMeas9:
+      anzByte = 0;
+      break;
+
+    case plptMeas9Ctrl4:
+      anzByte = 4;
+      break;
+
+    case plptMeas13:
+      anzByte = 0;
+      break;
+
+    default:
+      anzByte = 0;
+      break;
+  }
+
+  for (int i = 0; i < anzByte; i++)
+  {
+    dest[i] = slavePtr->result.plData[i+20];
+  }
+
+  return(anzByte);
+}
+
+
+// --------------------------------------------------------------------------
+// Debugging
+// --------------------------------------------------------------------------
+//
+dword BlePoll::debGetDword(int idx)
+{
+  dword retv = 0;
+
+  switch(idx)
+  {
+    case 0:
+      retv = plMode;
+      break;
+
+    case 1:
+      retv = cntAllRecs;
+      break;
+
+    case 2:
+      retv = cntAllNaks;
+      break;
+
+    case 3:
+      retv = cntAllTo;
+      break;
+
+    case 4:
+      retv = cntAlien;
+      break;
+
+    case 5:
+      retv = cntWrong;
+      break;
+
+    case 6:
+      retv = statistic.pollAcks;
+      break;
+
+    case 7:
+      retv = statistic.pollNaks;
+      break;
+
+    case 8:
+      retv = bleState;
+      break;
+
+    case 9:
+      retv = radio->getState();
+      break;
+  }
+
+  return(retv);
+}
+
+dword BlePoll::getStatistics(TxStatisticsPtr dest)
+{
+  *dest = statistic;
+  return(bleState);
+}
+
+
+SlavePtr      BlePoll::getSlavePtr(int idx)
+{
+  return(&slaveList[idx]);
+}
+
+PollStatePtr  BlePoll::getPollPtr(int idx)
+{
+  return(&pollList[idx]);
+}
+
+
+
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/BlePoll/BlePoll.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/BlePoll/BlePoll.h
new file mode 100644
index 0000000000000000000000000000000000000000..721d22c5c1b2fd65fc1415d7707fb2d93383e036
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/BlePoll/BlePoll.h
@@ -0,0 +1,513 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   BlePoll.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   1. September 2021
+//
+// Diese Bibliothek (Klassse) enthält diverse Ressourcen zur Kommunikation
+// über BLE-Funkmodule auf niedriger Ebene, also dem direkten Telegrammaustausch.
+// Darauf aufbauend sollen mehrkanalige Messeinrichtungen mit möglichst
+// geringen Latenzzeiten entwickelt werden.
+//
+
+#ifndef BlePoll_h
+#define BlePoll_h
+// ----------------------------------------------------------------------------
+
+#include "stddef.h"
+#include "string.h"
+#include "arduinoDefs.h"
+#include "bleSpec.h"
+#include "IntrfRadio.h"
+
+#define MAXSLAVE  100
+
+// Betriebsmodus (Polling und Slave)
+//
+typedef enum _PlMode
+{
+  plmIdle,        // Ruhezustand, Gerät nicht im Netz aktiv
+  plmTest,        // Low Level Tests
+  plmEmpty,       // Leeres Polling (Aufbau Adressliste)
+  plmScan,        // Daten aller aktiven Teilnehmer holen (Master)
+  plmSoaapM,      // Vollständiger Betrieb SOAAP (Master)
+  plmSoaapS,      // Vollständiger Betrieb SOAAP (Slave)
+  plmXchg         // Daten übertragen (Slave, beide Richtungen)
+} PlMode;
+
+// Grundsätzliche Datenstruktur für die Nutzdaten (ohne 6 Bytes Adresse)
+//
+typedef struct _PlPduBase   // maximale Länge Beacon = 31 Bytes
+{
+  byte  counter;      // zyklischer Telegrammmzähler
+  byte  type;         // Kennzeichnung der Datenstruktur (AppType)
+  byte  plData[29];   // weitere spezifische Nutzdaten
+} PlPduBase, *PlPduBasePtr;
+
+// Grundsätzliche Datenstruktur für Messwerte (ohne 6 Bytes Adresse)
+//
+typedef struct _PlPduMeas   // maximale Länge Beacon = 31 Bytes
+{
+  byte  counter;      // zyklischer Telegrammmzähler
+  byte  type;         // Kennzeichnung der Datenstruktur (AppType)
+  byte  appId;        // Kennzeichnung für Dateninhalte (PlpType)
+  byte  measCnt;      // Zähler für Messwertaktualisierung
+  byte  plData[27];   // weitere spezifische Nutzdaten
+} PlPduMeas, *PlPduMeasPtr;
+
+// Erweiterte Datenstruktur für die Nutzdaten (ohne 6 Bytes Adresse)
+// zur Zeit noch nicht genutzt
+//
+typedef struct _PlPduExtd   // grundsätzliche maximale Länge = 249 Bytes
+{
+  byte  counter;      // zyklischer Telegrammmzähler
+  byte  type;         // Kennzeichnung der Datenstruktur (AppType)
+  byte  plData[247];  // weitere spezifische Nutzdaten
+} PlPduExtd, *PlPduExtdPtr;
+
+// Datentypen (appId in plPduBase)
+//
+typedef enum _PlpType
+{
+  plptError,        // Fehler erkannt
+  plptEmpty,        // Leeres Telegramm (nur adresse)
+  plptBasic,        // Grundlegende Bytestruktur
+  plptFullMeas,     // Maximale Belegung mit 16-Bit Messwerten (word)
+  plptMeas3,        // 3 Messwerte (1 Raumsensor)
+  plptMeas6,        // 6 Messwerte (2 Raumsensoren)
+  plptMeas9,        // 9 Messwerte (3 Raumsensoren)
+  plptMeas9Ctrl4,   // 9 Messwerte + 4 Byte Steuerung
+  plptMeas10Ctrl4,  // 10 Messwerte + 4 Byte Steuerung
+  plptMeas12,       // 12 Messwerte (9 + 6 Byte Extradaten)
+  plptMeas13,       // 13 Messwerte (9 + 8 Byte Extradaten)
+  plptMeasX,        // Variable Anzahl Messwerte
+  plptCtrl0,        // Keine Steuerung
+  plptCtrl2,        // 2 Byte Steuerung
+  plptCtrl27,       // 27 Byte Steuerung
+  plptCtrlX,        // Variable Anzahl Steuerbytes
+  plptMsg           // (Quittierte) Meldung an Slave
+} PlpType, *PlpTypePtr;
+
+// Spezifische Datenstrukturen
+//
+typedef struct _PlpFullMeas   // Ausnahme für vordefinierte Spezialfälle
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  word    meas[14];   // Liste von 14 Messwerten
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    align;      // Wird nicht gesendet, kennzeichnet Alignement
+} PlpFullMeas, *PlpFullMeasPtr;
+
+typedef struct _PlpMeas3      // Länge 10 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    measCnt;    // Zähler für Messwertaktualisierung
+  word    meas[3];    // Liste von 3 Messwerten
+} PlpMeas3, *PlpMeas3Ptr;
+
+typedef struct _PlpMeas6      // Länge 16 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    measCnt;    // Zähler für Messwertaktualisierung
+  word    meas[6];    // Liste von 6 Messwerten
+} PlpMeas6, *PlpMeas6Ptr;
+
+typedef struct _PlpMeas9      // Länge 22 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    measCnt;    // Zähler für Messwertaktualisierung
+  word    meas[9];    // Liste von 9 Messwerten
+} PlpMeas9, *PlpMeas9Ptr;
+
+typedef struct _PlpM9C4      // Länge 26 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    measCnt;    // Zähler für Messwertaktualisierung
+  word    meas[9];    // Liste von 9 Messwerten
+  byte    ctrlPath;   // Steuerungspfad (für Verzweigungen/Funktionen)
+  byte    procCnt;    // Prozesszähler
+  byte    ctrl[2];    // Steuerungsdaten
+} PlpM9C4, *PlpM9C4Ptr;
+
+typedef struct _PlpMeas12      // Länge 28 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    measCnt;    // Zähler für Messwertaktualisierung
+  word    meas[12];   // Liste von 12 Messwerten
+} PlpMeas12, *PlpMeas12Ptr;
+
+typedef struct _PlpMeas13      // Länge 30 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    measCnt;    // Zähler für Messwertaktualisierung
+  word    meas[13];   // Liste von 13 Messwerten
+} PlpMeas13, *PlpMeas13Ptr;
+
+typedef struct _PlpCtrl2        // Länge 7 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    ctrlCnt;    // Zähler für Kommandoaktualisierung
+  byte    procCnt;    // Zähler für Prozessaktualisierung
+  byte    ctrl[2];    // Liste von 2 Steuerbytes
+} PlpCtrl2, *PlpCtrl2Ptr;
+
+typedef struct _PlpCtrl27        // Länge 31 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    ctrlCnt;    // Zähler für Kommandoaktualisierung
+  byte    procCnt;    // Zähler für Prozessaktualisierung
+  byte    ctrl[26];   // Liste von bis zu 26 Steuerbytes
+} PlpCtrl27, *PlpCtrl27Ptr;
+
+// Identifikator für die Art der Daten
+//
+typedef enum _MeasId
+{
+  app               // Gestaltung/Bedeutung der Daten aus Anwendung
+} MeasId, *MeasIdPtr;
+
+typedef struct _Slave
+{
+  dword     timeOut;        // Wartezeit beim Polling in Mikrosekunden
+  dword     cntTo;          // Zähler für ausbleibende Antworten
+  dword     cntErrCrc;      // Zähler für CRC-Fehler bei der Übertragung
+  dword     cntNakEP;       // Zähler für NAK-Antworten beim Empty-Polling
+  dword     cntAckDP;       // Zähler für ACK-Antworten beim Data-Polling
+  dword     cntLostPdu;     // Zähler für verlorene Telegramme
+  dword     cntLostMeas;    // Zähler für verlorene Messwerte
+  dword     delayCnt;       // Verzögerung (Polldurchläufe) vor Fehlerzählung
+  byte      adr;            // Adresse (Nummer) des Slave (1-255)
+  byte      area;           // Einsatzgebiet des Slave (Adresserweiterung)
+  byte      chn;            // Aktueller Übertragungskanal (0-39)
+  byte      pIdx;           // Index in der aktuellen Pollingliste
+  word      prioSet;        // Anfangspriorität (rel. Häufigkeit) beim Polling
+  word      minPrio;        // Minimale Priorität (maximale Prioritätszahl)
+  PlPduBase result;         // Daten vom Slave
+  PlPduBase control;        // Daten zum Slave
+  bool      newPdu;         // Kennzeichnung für neues Telegramm
+  bool      rspOk;          // Rücksetz-Kennnzeichnung für neues Telegramm
+  bool      newMeas;        // Kennzeichnung für neuen Messwert
+  byte      oldPduCount;    // Merker für die Telegrammüberwachung
+  byte      oldMeasCount;   // Merker für die Messwertüberwachung
+  byte      rspCtrlCount;   // Merker für Steuerungsüberwachung
+} Slave, *SlavePtr;
+
+
+typedef struct _PollInfo
+{
+  dword   aliens;     // Anzahl der netzfremden Aktivitäten
+  dword   wrongs;     // Anzahl ungewünschter Netzaktivitäten
+} PollInfo, *PollInfoPtr;
+
+
+typedef struct _PollState
+{
+  byte    slIdx;      // Index in der Slave-Liste
+  byte    status;     // Zustand
+  word    prioCnt;    // Prioritätszähler
+} PollState, *PollStatePtr;
+
+typedef bool (*cbDataPtr)(PlpType dataType, byte *dest);
+typedef bool (*cbCtrlPtr)(PlpType plpType, byte *dest, byte *src, int sSize);
+
+#define psSlaveWasPresent 0x01
+#define psSlaveIsPresent  0x02
+
+// ----------------------------------------------------------------------------
+//                            B l e P o l l
+// ----------------------------------------------------------------------------
+class BlePoll
+{
+public:
+  // -------------------------------------------------------------------------
+  // Öffentliche Datentypen
+  // -------------------------------------------------------------------------
+  //
+  typedef enum _ComType
+  {
+    ctMASTER,
+    ctSLAVE,
+    ctHybrid
+  } ComType;
+
+  // Identifikator für die Anwendung
+  //
+  typedef enum _AppType
+  {
+    atDefault,        // Standard-Default-Anwendung
+    atTestSend,       // einfacher Sendetest (Soaap)
+    atSOAAP,          // Steuerung optischer und akustischer Ausgaben für Performance-Künstler
+    atDevSOAAP,       // Entwicklerbetrieb für SOAAP
+    atDHA             // Dezentrale Hausautomatisierung
+  } AppType;
+
+  // --------------------------------------------------------------------------
+  // Öffentliche Daten
+  // --------------------------------------------------------------------------
+  //
+  bool    DataExchange;
+
+private:
+  // -------------------------------------------------------------------------
+  // Private Datentypen
+  // -------------------------------------------------------------------------
+  //
+
+  typedef void (BlePoll::*cbVector)(void);
+  typedef dword (*MicsecFuPtr)(void);
+
+  // --------------------------------------------------------------------------
+  // Lokale Daten
+  // --------------------------------------------------------------------------
+  //
+  IntrfRadio    *radio;
+  bcPdu         pduOut;
+  bcPdu         pduIn;
+  cbVector      nextState;
+  MicsecFuPtr   micSec;
+  cbDataPtr     cbData;
+  cbCtrlPtr     cbCtrl;
+  dword         toSet;
+  dword         toValue;
+  dword         cycleMics;
+  dword         cycleCnt;
+  dword         wdTimeOut;
+
+  int           chn;
+  int           adr;
+  int           area;
+  bool          master;
+  bool          eadr;
+  bool          nak;
+  bool          crcError;
+
+  PlMode        plMode;
+  PlMode        oldPlMode;
+  PlpType       plpType;
+
+  Slave         slaveList[MAXSLAVE+1];
+  SlavePtr      curSlave;
+  int           slaveIdx; // ist z.Zt. identisch mit Slaveadresse adr
+  PollState     pollList[MAXSLAVE+1];
+  PollStatePtr  curPoll;
+  int           pollIdx;
+  int           pollNr;
+  int           pollMaxNr;
+
+  int           maxAdr;
+  dword         cntPolling;
+  dword         cntAllRecs;
+  dword         cntAllTo;
+  bool          pollStop;
+  bool          pollStopped;
+
+  dword         cntAllNaks;
+  dword         cntAlien;
+  dword         cntWrong;
+  dword         cntWaitDisabled;
+
+  dword         bleState;
+  dword         runCounter;
+  bool          recStop;
+  bool          recStopped;
+
+  TxStatistics  statistic;
+  PlPduMeas     valuePdu;
+  PlpCtrl27     ctrlPdu;
+  bool          newValue;
+  bool          newCtrl;
+
+  // Einstellungen für den Anwendungsbetrieb
+  //
+  bool          fullCycle;      // Vollständige Anwendung (EP & Data)
+  int           epCycleTotal;   // Anzahl der leeren Pollings gesamt
+  int           epCycleRun;     // Anzahl der leeren Pollings nach Kontakt
+  dword         epTimeOut;      // Time-Out in Mikrosekunden
+
+
+  // --------------------------------------------------------------------------
+  // Lokale Funktionen
+  // --------------------------------------------------------------------------
+  //
+  void setPduAddress();
+  void setPduAddress(bcPduPtr pduPtr);
+  void setTimeOut(dword value);
+  bool timeOut();
+  bool getValues(bcPduPtr pduPtr, PlpType appId);
+  bool getCtrls(bcPduPtr pduPtr, PlpType appId);
+
+
+  // Zustandsmaschine
+  // -----------------------------
+  void smInit();
+  void smIdle();
+
+  // Leeres Polling Master
+  //
+  void smStartEP();
+  void smReqEadr();
+  void smWaitNak();
+  void smEndEP();
+
+  // Leeres Polling Slave
+  //
+  void smWaitEadr();
+  void smEvalPoll();
+
+  // Datenübertragung
+  //
+  void smStartCom();
+
+  // Master: Master -> Slave
+  //
+  void smReqComE();
+  void smWaitAckComE();
+  void smEndComE();
+
+  // Master: Slave -> Master
+  //
+  void smReqComS();
+  void smWaitAckComS();
+  void smEndComS();
+
+  // Slave: Master <-> Slave
+  //
+  void smStartComES();
+  void smWaitComES();
+  void smEvalComES();
+
+  // Test
+  //
+  void smStartTest();
+  void smWaitTest();
+  void smLoopTest();
+
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen
+  // --------------------------------------------------------------------------
+  BlePoll(IntrfRadio *refRadio, dword inCycleMics);
+  BlePoll(IntrfRadio *refRadio, MicsecFuPtr inMicroFu);
+  void init(IntrfRadio *refRadio, dword inCycleMics, MicsecFuPtr inMicroFu);
+
+  void begin(ComType typeIn, int adrIn, AppType appType, dword watchDog);
+  void setCbDataPtr(cbDataPtr cbPtr);
+  void setCbCtrlPtr(cbCtrlPtr cbPtr);
+
+  // --------------------------------------------------------------------------
+  // Konfiguration
+  // --------------------------------------------------------------------------
+  //
+  void setPollAddress(int chnIn, int adrIn, int areaIn, bool masterIn, bool eadrIn, bool nakIn);
+  void setEmptyPollParams(int cycleTotal, int cycleRun, dword timeOut);
+  void setDataPollParams(int slAdr, int prio, int minPrio, dword timeOut);
+
+  // --------------------------------------------------------------------------
+  // Steuerung des Telegrammaustausches (Polling)
+  // --------------------------------------------------------------------------
+  //
+  void run();       // Ablaufsteuerung (CPU-Übergabe) dieses Moduls
+/**
+ * @brief Sendet neue Steuerungsdaten an einen Slave
+ * 
+ * @param adr Addresse d. zu Steuernden Slaves
+ * @param ctrlList Liste mit Bytes zum Austausch der Steuerbytes
+ * @param nr Anzahl d. Steuerbytes
+ */
+  void updControl(int adr, byte *ctrlList, int nr);   // neue Steuerungsdaten
+  //
+/**
+ * @brief Prüft ob Steuerungsdaten übertragen wurden
+ * 
+ * @param adr Adresse d. Slaves
+ * @return true Erfolgreiche übertragung
+ * @return false Fehler in Übertragung / Falsche Adresse
+ */
+  bool ackTrans(int adr);         // Bestätigung Steuerungsdaten übertragen
+  /**
+ * @brief Überprüft ob Steuerungsdaten korrekt übertragen wurden
+ * 
+ * @param adr Adresse d. Slavrs
+ * @return true Daten erfolgreich verarbeitet
+ * @return false Fehler in Übertragung
+ */
+  bool ackControl(int adr);       // Bestätigung Steuerung ausgeführt
+
+
+  // Test
+  //
+
+  // Leeres Polling
+  //
+  void stopEP();
+  void resumeEP();
+  bool stoppedEP();
+
+  // Empfangsbetrieb beim Slave
+  //
+  void stopSR();
+  void resumeSR();
+  bool stoppedSR();
+
+  // Laufender Betrieb
+  //
+  void start(PlMode inPlMode);
+
+
+
+  // --------------------------------------------------------------------------
+  // Zugriff auf Polling-Informationen
+  // --------------------------------------------------------------------------
+  //
+  int   getSlaveList(byte *dest, int maxByte);
+  void  resetPollCounters();
+
+  // --------------------------------------------------------------------------
+  // Zugriff auf Slavedaten
+  // --------------------------------------------------------------------------
+  // Der Index wird von 0 an ausgewertet. Allerdings ist [0] in der Slave-Liste
+  // auf den Index [1] abzubilden, weil Slave[0] für besondere Aufgaben
+  // reserviert und für den Anwender nicht zugänglich ist.
+  //
+  bool      measAvail(int slIdx);   // Feststellen, ob neue Messwerte da sind
+  int       getArea(int slIdx);     // Wert der Area auslesen
+  PlpType   getAppId(int slIdx);    // Wert der AppId (BlePoll) auslesen
+  int       getMeas(int slIdx, byte *dest);    // Messwerte übergeben
+  int       getCtrlM(int slIdx, byte *dest);
+
+
+  // --------------------------------------------------------------------------
+  // Debugging
+  // --------------------------------------------------------------------------
+  //
+  dword debGetDword(int idx);
+  dword getStatistics(TxStatisticsPtr dest);
+
+  SlavePtr      getSlavePtr(int idx);
+  PollStatePtr  getPollPtr(int idx);
+};
+
+
+// ----------------------------------------------------------------------------
+#endif // BlePoll_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/BlePoll/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/BlePoll/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..477225b9ff50f19fdf4a1ffb19f791f80efa4870
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/BlePoll/library.json
@@ -0,0 +1,4 @@
+{
+    "name": "BlePoll",
+    "version": "0.0.0+20220804174235"
+  }
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/ComRingBuf/ComRingBuf.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/ComRingBuf/ComRingBuf.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..66a9784b5507d680cc700b0f7fe5e68379da7e3e
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/ComRingBuf/ComRingBuf.cpp
@@ -0,0 +1,782 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   ComRingBuf.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   21. November 2021
+//
+// Der Inhalt dieser Datei sind Festlegungen zur Gestaltung eines Ringpuffers.
+//
+
+#include "ComRingBuf.h"
+
+  // --------------------------------------------------------------------------
+  // Initialisierungen
+  // --------------------------------------------------------------------------
+
+  ComRingBuf::ComRingBuf()
+  {
+    rbReadIdx = 0;
+    rbWriteIdx = 0;
+    sbReadIdx = 0;
+    sbWriteIdx = 0;
+    newLineMode = NewLineModeNL;
+  }
+
+  void ComRingBuf::begin(IntrfSerial *ser)
+  {
+    serIf = ser;
+  }
+
+  // --------------------------------------------------------------------------
+  // Konfiguration
+  // --------------------------------------------------------------------------
+  //
+  void ComRingBuf::setNewLineMode(byte nlMode)
+  {
+    newLineMode = nlMode;
+  }
+
+
+  // --------------------------------------------------------------------------
+  // Schnittstellen
+  // --------------------------------------------------------------------------
+  //
+  // Byte aus dem Sendepuffer lesen
+  //
+  bool  ComRingBuf::getByteSnd(byte *dest)
+  {
+    if(sbReadIdx == sbWriteIdx) return(false);
+
+    *dest = sndBuffer[sbReadIdx];
+    sbReadIdx++;
+    if(sbReadIdx >= sbSize)
+      sbReadIdx = 0;
+    return(true);
+  }
+
+  // Byte in den Empfangspuffer schreiben
+  //
+  void  ComRingBuf::putByteRec(byte b)
+  {
+    int space = rbReadIdx - rbWriteIdx - 1;
+    if(space == 0) return;
+  }
+
+
+// ----------------------------------------------------------------------------
+// Writing and reading data via circular buffer (default usage)
+// ----------------------------------------------------------------------------
+//
+
+// ----------------------------------------------------------------------------
+// Lesen (Empfangsvorgänge)
+// ----------------------------------------------------------------------------
+
+void  ComRingBuf::setReadBuffer(int size, byte *bufPtr)
+{
+  recBuffer = bufPtr;
+  rbSize = size;
+  rbReadIdx = 0;
+  rbWriteIdx = 0;
+}
+
+int   ComRingBuf::getChr()
+{
+  int retv;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(EOF);
+
+  retv = recBuffer[rbReadIdx];
+  rbReadIdx++;
+  if(rbReadIdx >= rbSize)
+    rbReadIdx = 0;
+  return(retv);
+}
+
+void  ComRingBuf::clrRecBuf()
+{
+  rbReadIdx  = 0;
+  rbWriteIdx = 0;
+}
+
+int   ComRingBuf::getAll(byte *buffer)
+{
+  int   count, i;
+  int   tmpInt;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(EOF);
+
+  tmpInt = rbWriteIdx - rbReadIdx;
+  if(tmpInt < 0)
+    count = tmpInt + rbSize;
+  else
+    count = tmpInt;
+
+  for(i = 0; i < count; i++)
+  {
+    buffer[i] = recBuffer[rbReadIdx];
+    rbReadIdx++;
+    if(rbReadIdx >= rbSize)
+      rbReadIdx = 0;
+  }
+
+  return(count);
+}
+
+int   ComRingBuf::getCount(int len, byte *buffer)
+{
+  int  count, i;
+  int  tmpInt;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(0);
+
+  tmpInt = rbWriteIdx - rbReadIdx;
+  if(tmpInt < 0)
+    count = tmpInt + rbSize;
+  else
+    count = tmpInt;
+
+  if(len > count)
+    len = count;
+
+  for(i = 0; i < len; i++)
+  {
+    buffer[i] = recBuffer[rbReadIdx];
+    rbReadIdx++;
+    if(rbReadIdx >= rbSize)
+      rbReadIdx = 0;
+  }
+
+  return(len);
+}
+
+int   ComRingBuf::getCountStr(int len, char *buffer)
+{
+  int nrChar;
+
+  nrChar = getCount(len, (uint8_t *) buffer);
+  if(nrChar == EOF) return(EOF);
+
+  buffer[nrChar] = 0;
+  return(nrChar);
+}
+
+int   ComRingBuf::getLine(char *buffer)
+{
+  bool      eol;
+  int       count, i;
+  int       tmpInt;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(0);
+
+  tmpInt = rbWriteIdx - rbReadIdx;
+  if(tmpInt < 0)
+    count = tmpInt + rbSize;
+  else
+    count = tmpInt;
+
+  eol = false;
+
+  for(i = 0; i < count; i++)
+  {
+    buffer[i] = recBuffer[rbReadIdx];
+    if(!eol)
+    {
+    if(buffer[i] == '\r' || buffer[i] == '\n')
+      eol = true;
+    }
+    else
+    {
+      if(buffer[i] != '\r' && buffer[i] != '\n')
+        break;
+    }
+    rbReadIdx++;
+    if(rbReadIdx >= rbSize)
+      rbReadIdx = 0;
+  }
+
+  if(!eol) return(0);
+
+  buffer[i] = 0;
+  return(i);
+}
+
+int   ComRingBuf::getLineDec(int *intValue)
+{
+  bool      eol, inVal;
+  int       count, i, j;
+  int       tmpInt;
+  char      c;
+  char      buffer[32];
+
+  if(rbReadIdx == rbWriteIdx)
+    return(0);
+
+  tmpInt = rbWriteIdx - rbReadIdx;
+  if(tmpInt < 0)
+    count = tmpInt + rbSize;
+  else
+    count = tmpInt;
+
+  if(count > 30)
+    count = 30;
+
+  eol = false;
+
+  j = 0;
+  inVal = false;
+
+  for(i = 0; i < count; i++)
+  {
+    c = recBuffer[rbReadIdx];
+    if(!inVal)
+    {
+      if(c > '9')
+      {
+        rbReadIdx++;
+        if(rbReadIdx >= rbSize)
+          rbReadIdx = 0;
+        continue;
+      }
+      inVal = true;
+    }
+
+    if(!eol)
+    {
+    if(c == '\r' || c == '\n')
+      eol = true;
+    }
+    else
+    {
+      if(c != '\r' && c != '\n')
+        break;
+    }
+    rbReadIdx++;
+    if(rbReadIdx >= rbSize)
+      rbReadIdx = 0;
+    buffer[j++] = c;
+  }
+
+  if(!eol) return(0);
+
+  buffer[j] = 0;
+  *intValue = atoi(buffer);
+  return(i);
+}
+
+char ComRingBuf::getC()
+{
+  char retC;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(0);
+
+  retC = recBuffer[rbReadIdx];
+  rbReadIdx++;
+  if(rbReadIdx >= rbSize)
+    rbReadIdx = 0;
+
+  return(retC);
+}
+
+
+int   ComRingBuf::waitLine(int waitLoop, char *buffer)
+{
+  char inChar;
+  bool eol;
+
+  if(loopCount == 0)
+  {
+    tmpIdx = 0;
+  }
+
+  tmpVal = inCount();
+  if(tmpVal < 1)
+  {
+    loopCount++;
+    if(loopCount < waitLoop)
+      return(0);
+    else
+    {
+      loopCount = 0;
+      return(EOF);
+    }
+  }
+
+  eol = false;
+
+  for(int i = 0; i < tmpVal; i++)
+  {
+    inChar = getC();
+    buffer[tmpIdx++] = inChar;
+    if(inChar == '\r' || inChar == '\n')
+    {
+      eol = true;
+      break;
+    }
+  }
+
+  if(eol)
+  {
+    buffer[tmpIdx] = 0;
+    loopCount = 0;
+    return(tmpIdx);
+  }
+
+  return(0);
+}
+
+//int   ComRingBuf::waitLineDec(int waitLoop, int *intValue)
+//{
+//};
+
+int   ComRingBuf::chkLine(char *rsp)
+{
+  int     i,chkVal;
+  char    chkChar;
+
+  chkVal = inCount();
+  if(chkVal <= strlen(rsp))
+    return(0);
+
+  chkVal = 0;
+
+  for(i = 0; i < strlen(rsp); i++)
+  {
+    chkChar = getC();
+    if(rsp[i] != chkChar)
+    {
+      chkVal = -100000;
+      break;
+    }
+    else
+      chkVal++;
+  }
+
+  if(chkVal < 0) chkVal = EOF;
+
+  while( (recBuffer[rbReadIdx] == '\r' || recBuffer[rbReadIdx] == '\n' )
+         && (rbReadIdx != rbWriteIdx))
+  {
+    rbReadIdx++;
+    if(rbReadIdx >= rbSize)
+      rbReadIdx = 0;
+  }
+
+  return(chkVal);
+}
+
+int   ComRingBuf::chkBuf(char *rsp)
+{
+  int     i,chkVal;
+  char    chkChar;
+
+  chkVal = inCount();
+  if(chkVal < strlen(rsp))
+    return(0);
+
+  chkVal = 0;
+
+  for(i = 0; i < strlen(rsp); i++)
+  {
+    chkChar = getC();
+    if(rsp[i] != chkChar)
+      chkVal = -100;
+    else
+      chkVal++;
+  }
+
+  return(chkVal);
+}
+
+int   ComRingBuf::waitAll(int waitLoop, byte *buffer)
+{
+}
+
+int   ComRingBuf::waitChkBuf(int waitLoop, char *rsp)
+{
+  int   i;
+  int   chkVal;
+  char  chkChar;
+
+  if(loopCount == 0)
+  {
+    tmpVal = strlen(rsp);
+  }
+
+  chkVal = inCount();
+  if(chkVal < tmpVal)
+  {
+    loopCount++;
+    if(loopCount < waitLoop)
+      return(0);
+    else
+    {
+      loopCount = 0;
+      return(-10);
+    }
+  }
+
+  chkChar = getC();
+
+  if(rsp[0] != chkChar)
+    return(0);
+
+  if(tmpVal == 1)
+  {
+     loopCount = 0;
+     return(1);
+  }
+
+  chkVal = 1;
+
+  for(i = 1; i < tmpVal; i++)
+  {
+    chkChar = getC();
+    if(rsp[i] != chkChar)
+      return(0);
+    else
+      chkVal++;
+  }
+
+  loopCount = 0;
+  return(chkVal);
+}
+
+int   ComRingBuf::inCount(void)
+{
+  int count = rbWriteIdx - rbReadIdx;
+  if(count < 0)
+    count += rbSize;
+  return(count);
+}
+
+int   ComRingBuf::getRestChar(byte tagChr, int len, byte *buffer)
+{
+  int       count, i, j;
+  byte      inChr;
+  int       tmpInt;
+  bool      tagged;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(0);
+
+  tmpInt = rbWriteIdx - rbReadIdx;
+  if(tmpInt < 0)
+    count = tmpInt + rbSize;
+  else
+    count = tmpInt;
+
+  if(len > count)
+    len = count;
+
+  tagged = false;
+  j = 0;
+
+  for(i = 0; i < len; i++)
+  {
+    inChr = recBuffer[rbReadIdx];
+    rbReadIdx++;
+    if(rbReadIdx >= rbSize)
+      rbReadIdx = 0;
+
+    if(!tagged)
+    {
+      if(inChr != tagChr)
+        continue;
+
+      tagged = true;
+      continue;
+    }
+
+    buffer[j++] = inChr;
+  }
+
+  if(!tagged) j = -1;
+
+  return(j);
+}
+
+int   ComRingBuf::getRestStr(char *tagStr, int len, byte *buffer)
+{
+  int       count, i, j, tmpIdx;
+  byte      inChr;
+  int       tmpInt;
+  bool      tagged;
+  int       tagLen;
+  int       tagIdx;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(0);
+
+  tmpIdx = rbReadIdx;
+  tmpInt = rbWriteIdx - tmpIdx;
+
+  if(tmpInt < 0)
+    count = tmpInt + rbSize;
+  else
+    count = tmpInt;
+
+  if(len > count)
+    len = count;
+
+  tagged = false;
+  j = 0;
+
+  tagLen = (int) strlen(tagStr);
+  tagIdx = 0;
+
+  if(len < tagLen)
+    return(0);
+
+  for(i = 0; i < len; i++)
+  {
+    inChr = recBuffer[tmpIdx];
+    tmpIdx++;
+    if(tmpIdx >= rbSize)
+      tmpIdx = 0;
+
+    if(!tagged)
+    {
+      if(inChr != tagStr[tagIdx])
+      {
+        tagIdx = 0;
+        continue;
+      }
+
+      tagIdx++;
+
+      if(tagIdx == tagLen)
+        tagged = true;
+      continue;
+    }
+
+    buffer[j++] = inChr;
+  }
+
+  if(!tagged) return(EOF);
+  else
+  {
+    rbReadIdx = tmpIdx;
+    return(j);
+  }
+}
+
+int   ComRingBuf::reqChkLine(char *req, char *rsp)
+{
+  int   i;
+  int   chkVal;
+  char  chkChar;
+
+  switch(reqChkState)
+  {
+    case 0:
+      rbReadIdx  = 0;
+      rbWriteIdx = 0;
+      chkVal = putLine(req);
+      if(chkVal <= 0)
+        return(EOF);
+      tmpVal = strlen(rsp);
+      reqChkState = 1;
+      return(0);
+
+    case 1:
+      chkVal = inCount();
+      if(chkVal <= tmpVal)
+        return(0);
+      chkVal = 0;
+
+      for(i = 0; i < tmpVal; i++)
+      {
+        chkChar = getC();
+        if(rsp[i] != chkChar)
+          chkVal = -100;
+        else
+          chkVal++;
+      }
+
+      while(     (recBuffer[rbReadIdx] == '\r' || recBuffer[rbReadIdx] == '\n')
+          && (rbReadIdx != rbWriteIdx) )
+      {
+        rbReadIdx++;
+        if(rbReadIdx >= rbSize)
+          rbReadIdx = 0;
+      }
+
+      reqChkState = 0;
+      return(chkVal);
+  }
+
+  return(-1000);    // internal error with <reqChkState>
+}
+
+// ----------------------------------------------------------------------------
+// Schreiben (Sendevorgänge)
+// ----------------------------------------------------------------------------
+
+void  ComRingBuf::setWriteBuffer(int size, byte *bufPtr)
+{
+  sndBuffer = bufPtr;
+  sbSize = size;
+  sbReadIdx = 0;
+  sbWriteIdx = 0;
+}
+
+int   ComRingBuf::putChr(int chr)
+{
+  int       space;
+  bool      txDone;
+
+  if(sndBuffer == NULL) return(EOF);
+  if(serIf == NULL) return(EOF);
+
+  space = getSpace();
+
+  if(space == 0)
+  {
+    // Wenn der Sendepuffer voll ist, dann kann der Entlader feststecken
+    serIf->resuSend();
+    return(EOF);
+  }
+
+  if(sbReadIdx == sbWriteIdx)
+  {
+    // Wenn der Sendepuffer leer ist, dann kann das Zeichen evt.
+    // direkt gesendet werden.
+    txDone = serIf->condSend(chr);
+    if(txDone) return(chr);
+  }
+
+  putBufB(chr);
+  return(chr);
+}
+
+int   ComRingBuf::putStr(char *msg)
+{
+  int sIdx = 0;
+
+  if(sndBuffer == NULL) return(EOF);
+  if(serIf == NULL) return(EOF);
+
+  int space = getSpace();
+  int len = strlen(msg);
+  if(space < len)
+  {
+    serIf->resuSend();
+    return(EOF);
+  }
+
+  if(sbReadIdx == sbWriteIdx)
+  {
+    // Wenn der Sendepuffer leer ist, dann kann das erste Zeichen evt.
+    // direkt gesendet werden.
+    if(serIf->condSend(msg[0]))
+      sIdx = 1;
+  }
+
+  for (int i = sIdx; i < len; i++)
+   {
+     sndBuffer[sbWriteIdx] = msg[i];
+     sbWriteIdx++;
+     if(sbWriteIdx >= sbSize)
+       sbWriteIdx = 0;
+   }
+
+  return(len);
+}
+
+int   ComRingBuf::putSeq(byte *msg, int n)
+{
+  int sIdx = 0;
+
+  if(sndBuffer == NULL) return(EOF);
+  if(serIf == NULL) return(EOF);
+
+  int space = getSpace();
+
+  if(space < n)
+  {
+    serIf->resuSend();
+    return(EOF);
+  }
+
+  if(sbReadIdx == sbWriteIdx)
+  {
+    // Wenn der Sendepuffer leer ist, dann kann das erste Zeichen evt.
+    // direkt gesendet werden.
+    if(serIf->condSend(msg[0]))
+      sIdx = 1;
+  }
+
+  for (int i = sIdx; i < n; i++)
+   {
+     sndBuffer[sbWriteIdx] = msg[i];
+     sbWriteIdx++;
+     if(sbWriteIdx >= sbSize)
+       sbWriteIdx = 0;
+   }
+
+  return(n);
+
+}
+
+int   ComRingBuf::putNL()
+{
+  int retv = 0;
+
+  if(newLineMode & NewLineModeCR)
+  {
+    putBufB('\r');
+    retv++;
+  }
+
+  if(newLineMode & NewLineModeNL)
+  {
+    putBufB('\n');
+    retv++;
+  }
+
+  return(retv);
+}
+
+int   ComRingBuf::putLine(char *msg)
+{
+  int retv, nl;
+
+  retv = putStr(msg);
+  if(retv < 0)
+    return(retv);
+
+  nl = putNL();
+  return(retv);
+}
+
+//int   ComRingBuf::putLine(char *msg, char c)
+//{
+//}
+
+//int   ComRingBuf::putLine(char *msg, int n)
+//{
+//}
+
+
+  // --------------------------------------------------------------------------
+  // Debugging
+  // --------------------------------------------------------------------------
+  //
+
+
+
+
+
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/ComRingBuf/ComRingBuf.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/ComRingBuf/ComRingBuf.h
new file mode 100644
index 0000000000000000000000000000000000000000..bd8ee0c8e604ac8ca5a6c344ca9966bb856f365b
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/ComRingBuf/ComRingBuf.h
@@ -0,0 +1,227 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   ComRingBuf.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   21. November 2021
+//
+// Der Inhalt dieser Datei sind Festlegungen zur Gestaltung eines Ringpuffers.
+//
+
+#ifndef ComRingBuf_h
+#define ComRingBuf_h
+// ----------------------------------------------------------------------------
+
+#include "stddef.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+#include "arduinoDefs.h"
+#include "IntrfBuf.h"
+#include "IntrfSerial.h"
+
+#define NewLineModeCR     0x01
+#define NewLineModeNL     0x02
+
+// ----------------------------------------------------------------------------
+//                            C o m R i n g B u f
+// ----------------------------------------------------------------------------
+class ComRingBuf : IntrfBuf
+{
+private:
+  // --------------------------------------------------------------------------
+  // Lokale Daten
+  // --------------------------------------------------------------------------
+  //
+  // Zugang zur Peripherie
+  //
+  IntrfSerial *serIf;
+
+  // Lesen und Schreiben von Zeichen (Bytes)
+  //
+  byte      *ptrSend;         // Der (veraenderliche) Sendezeiger
+  byte      *ptrRec;          // Der (veraenderliche) Empfangszeiger
+  int       maxRec;           // Maximale Anzahl zu empfangender Bytes
+  byte      endChrRec;        // Abschlusszeichen beim Empfang
+  byte      condMaskCom;      // Bedingungen fuer den Datenaustausch
+  byte      newLineMode;      // Art für eine neue Zeile (CR/LF)
+
+  byte      *recBuffer;       // Receive ring buffer start address
+  word      rbReadIdx;        // Read index
+  word      rbWriteIdx;       // Write index
+  word      rbSize;           // Buffer size
+
+  byte      *sndBuffer;       // Transmit ring buffer start address
+  word      sbReadIdx;        // Read index
+  word      sbWriteIdx;       // Write index
+  word      sbSize;           // Buffer size
+
+  int       loopCount;        // For internal time out checking
+  int       reqChkState;      // State of request/check procedure
+  int       tmpVal;           // Variable for temporary data storage
+  int       tmpIdx;           // Variable for temporary array index
+
+
+  // --------------------------------------------------------------------------
+  // Lokale Funktionen
+  // --------------------------------------------------------------------------
+  //
+  char  getC();
+  int   putNL();
+
+  // --------------------------------------------------------------------------
+  // Inline-Funktionen
+  // --------------------------------------------------------------------------
+  //
+  void putBufB(byte b)
+  {
+    sndBuffer[sbWriteIdx] = b;
+    sbWriteIdx++;
+    if(sbWriteIdx >= sbSize)
+      sbWriteIdx = 0;
+  }
+
+  int getSpace()
+  {
+    int space = sbReadIdx - sbWriteIdx - 1;
+    if(space < 0) space += sbSize;
+    return(space);
+  }
+
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen
+  // --------------------------------------------------------------------------
+  ComRingBuf();
+
+  void begin(IntrfSerial *ser);
+
+  // --------------------------------------------------------------------------
+  // Konfiguration
+  // --------------------------------------------------------------------------
+  //
+  void setNewLineMode(byte nlMode);
+
+  // --------------------------------------------------------------------------
+  // Schnittstellen
+  // --------------------------------------------------------------------------
+  //
+  bool  getByteSnd(byte *dest);
+  void  putByteRec(byte b);       // Byte vom Empfang an Puffer geben
+
+
+  // Zuweisen eines Speichers (*bufPtr) der Größe size für den Lesepuffer
+  //
+  void  setReadBuffer(int size, byte *bufPtr);
+
+
+  // --------------------------------------------------------------------------
+  // Steuerung
+  // --------------------------------------------------------------------------
+  //
+
+  // ----------------------------------------------
+  // Ein einzelnes Zeichen aus dem Ringpuffer lesen
+  // ----------------------------------------------
+  // Rückgabe EOF (-1), wenn kein Zeichen vorhanden
+  // sonst das älteste Zeichen aus dem Ringpuffer
+  //
+  int   getChr();
+
+  // ----------------------------------------------
+  // Löschen des Emmpfangspuffers
+  // ----------------------------------------------
+  //
+  void  clrRecBuf();
+
+  // --------------------------------------------------
+  // Alle empfangenen Zeichen aus dem Ringpuffer lesen
+  // --------------------------------------------------
+  // Rückgabe EOF (-1), wenn kein Zeichen vorhanden
+  // sonst die Anzahl der empfangenen Zeichen
+  //
+  int   getAll(byte *buffer);
+
+  // ----------------------------------------------------------
+  // Begrenzte Anzahl empfangener Zeichen aus Ringpuffer lesen
+  // ----------------------------------------------------------
+  // Rückgabe EOF (-1), wenn kein Zeichen vorhanden
+  // sonst die Anzahl der ausgelesenen Zeichen
+  //
+  int   getCount(int count, byte *buffer);
+
+  // ------------------------------------------------------------------------
+  // Begrenzte Anzahl Zeichen als 0-terminierten String aus Ringpuffer lesen
+  // ------------------------------------------------------------------------
+  // Rückgabe EOF (-1), wenn kein Zeichen vorhanden
+  // sonst die Anzahl der ausgelesenen Zeichen
+  //
+  int   getCountStr(int count, char *buffer);
+
+  // ---------------------------------------------------------
+  // Die nächste Zeile (Zeichen bis CR und/oder LF) als String
+  // ---------------------------------------------------------
+  // Rückgabe EOF (-1), wenn kein Zeichen vorhanden
+  // sonst die Anzahl der ausgelesenen Zeichen
+  //
+  int   getLine(char *buffer);
+
+  // -----------------------------------------------
+  // Die nächste im Puffer enthaltene Zeile auslesen
+  // und die darin enthaltene Dezimalzahl übergeben
+  // -----------------------------------------------
+  // Rückgabe EOF (-1), wenn kein Zeichen vorhanden
+  // sonst die Anzahl der ausgelesenen Zeichen
+  // oder 0, wenn keine Dezimalzahl enthalten war
+  //
+  int   getLineDec(int *intValue);
+
+  // ---------------------------------------------------------
+  // Warten auf Zeile (Zeichen bis CR und/oder LF) als String
+  // ---------------------------------------------------------
+  // Rückgabe EOF (-1), wenn Wartezyklen verstrichen
+  // Rückgabe 0, solange kein Zeilenende gelesen
+  // sonst die Anzahl der ausgelesenen Zeichen
+  //
+  int   waitLine(int waitLoop, char *buffer);
+
+  //int   waitLineDec(int waitLoop, int *intValue);
+
+  // ---------------------------------------------------------
+  // Testen der Zeile im Puffer
+  // ---------------------------------------------------------
+  // Rückgabe 0, wenn Teststring (noch) nicht enthalten
+  // sonst die Länge des Teststring
+  // EOF, wenn Zeile im Puffer (gelöscht) nicht passte
+  //
+  int   chkLine(char *rsp);
+
+  int   chkBuf(char *rsp);
+  int   waitAll(int waitLoop, byte *buffer);
+  int   waitChkBuf(int waitLoop, char *rsp);
+  int   inCount(void);
+  int   getRestChar(byte tagChr, int len, byte *buffer);
+  int   getRestStr(char *tagStr, int len, byte *buffer);
+  int   reqChkLine(char *req, char *rsp);
+
+  void  setWriteBuffer(int size, byte *bufPtr);
+  int   putChr(int chr);
+  int   putStr(char *msg);
+  int   putSeq(byte *msg, int n);
+  int   putLine(char *msg);
+  //int   putLine(char *msg, char c);
+  //int   putLine(char *msg, int n);
+
+  // --------------------------------------------------------------------------
+  // Debugging
+  // --------------------------------------------------------------------------
+  //
+
+};
+
+
+// ----------------------------------------------------------------------------
+#endif // beacon_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/ComRingBuf/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/ComRingBuf/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..054f2c8c3093791dc7ce4f7de879b26a019e900b
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/ComRingBuf/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "BlePoll",
+  "version": "0.0.0+20220804174235"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/LoopCheck/LoopCheck.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/LoopCheck/LoopCheck.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b01795b4f033ebb0286340840a3b1507c6ef0302
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/LoopCheck/LoopCheck.cpp
@@ -0,0 +1,838 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Software Loop Checking and Timing
+// Datei:   LoopCheck.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (siehe Wikipedia: Creative Commons)
+//
+
+#include "LoopCheck.h"
+
+
+  // -------------------------------------------------------------------------
+  // Konstruktoren und Initialisierungen
+  // -------------------------------------------------------------------------
+  //
+  LoopCheck::LoopCheck()
+  {
+    firstLoop       = true;
+    taskHappened    = false;
+    toggleMilli     = true;
+
+    initStatistics();
+    initTasks();
+    initClock();
+  }
+
+  void LoopCheck::initStatistics()
+  {
+    backgroundMicros        = 0;
+    loopMicros              = 0;
+    loopStartMicros         = 0;
+    loopEndMicros           = 0;
+    backgroundMaxMicros     = 0;
+    backgroundMinMicros     = (unsigned long) -1;
+    backgroundAvgMicros     = 0;
+    loopMaxMicros           = 0;
+    loopMinMicros           = (unsigned long) -1;
+    loopAvgMicros           = 0;
+    loopCounter             = 0;
+    periodFailCount         = 0;
+    periodMaxMicros         = 0;
+    periodMinMicros         = (unsigned int) -1;
+    periodMicros            = 0;
+    periodFailAlarm         = false;
+
+    year    = 0;
+    day     = 0;
+    hour    = 0;
+    min     = 0;
+    sec     = 0;
+    msec    = 0;
+
+    measureRuntime = 0;
+
+    calcAvgCounter = 0;
+  }
+
+  void LoopCheck::initTasks()
+  {
+    for (int i = 0; i < NrOfTimerTasks; i++)
+    {
+      timerTaskList[i].counterStarted    = false;
+      timerTaskList[i].finished          = false;
+      timerTaskList[i].firstRun          = true;
+      timerTaskList[i].runCounter        = 0;
+    }
+
+    for( int i = 0; i < NrOfOnceTasks; i++)
+    {
+      onceTaskList[i].finished      = false;
+      onceTaskList[i].firstRun      = true;
+      onceTaskList[i].waitCounter   = 0;
+    }
+  }
+
+  void LoopCheck::initClock()
+  {
+    strcpy(dateTimeStr,"2017-12-07T17:11:35.456+00:00");
+    dtYear      = 2017;
+    dtMonth     = 12;
+    dtDay       = 7;
+    dtHour      = 17;
+    dtMin       = 11;
+    dtSec       = 35;
+    dtmSec      = 456;
+  }
+
+  // -------------------------------------------------------------------------
+  // Anwenderfunktionen
+  // -------------------------------------------------------------------------
+  //
+
+  void LoopCheck::begin()
+  {
+    unsigned int    cycleMillis;
+    unsigned int    restMicros;
+    unsigned int    tmpInt;
+    unsigned int    tmpInt100, tmpInt10, tmpInt1;
+    div_t           divResult;
+
+    loopStartMicros = SYSMICSEC;
+    clockCycleMicros = loopStartMicros - lastClockMicros + lastRestMicros;
+    //
+    // Zeit seit dem letzten Aufruf von begin()
+
+  
+    //
+    if(firstLoop == true)
+    {
+      clockCycleMicros = 0;
+      lastClockMicros = loopStartMicros;
+    }
+
+    // Aufteilen in Millisekunden und Mikrosekunden
+    //
+    divResult = DIV((int) clockCycleMicros, 1000);
+
+    restMicros = divResult.rem;
+    cycleMillis = divResult.quot;
+
+    if(cycleMillis > 0)
+    {
+      lastRestMicros = restMicros;
+      lastClockMicros = loopStartMicros;
+      msec += cycleMillis;
+      dtmSec += cycleMillis;
+
+      // Betriebsstundenzähler
+      //
+      if(msec >= 1000)
+      {
+        msec -= 1000;
+        sec++;
+        measureRuntime++;
+
+        if(sec == 60)
+        {
+          sec = 0;
+          min++;
+          if(min == 60)
+          {
+            min = 0;
+            hour++;
+            if(hour == 24)
+            {
+              hour = 0;
+              day++;
+              if(day == 365)
+              {
+                day = 0;
+                year++;
+              }
+            }
+          }
+        }
+      }
+
+      // Software-Uhr
+      //
+      if(dtmSec >= 1000)
+      {
+        dtmSec -= 1000;
+
+        dtSec++;
+        dateTimeStr[20] = '0';
+        dateTimeStr[21] = '0';
+        dateTimeStr[22] = '0';
+
+        if(dtSec == 60)
+        {
+          dtSec = 0;
+          dtMin++;
+          dateTimeStr[17] = '0';
+          dateTimeStr[18] = '0';
+
+          if(dtMin == 60)
+          {
+            dtMin = 0;
+            dtHour++;
+            dateTimeStr[14] = '0';
+            dateTimeStr[15] = '0';
+
+            if(dtHour == 24)
+            {
+              dtHour = 0;
+              dtDay++;
+              dateTimeStr[11] = '0';
+              dateTimeStr[12] = '0';
+
+              if  (
+                       (dtDay == (febLen + 1) && dtMonth == 2)
+                    || (dtDay == 31 && (dtMonth == 4 ||
+                                        dtMonth == 6 ||
+                                        dtMonth == 9 ||
+                                        dtMonth == 11))
+                    || dtDay == 32
+                  )
+              {
+                dtDay = 1;
+                dtMonth++;
+                dateTimeStr[8] = '0';
+                dateTimeStr[9] = '1';
+
+                if(dtMonth == 13)
+                {
+                  dtMonth = 1;
+                  dtYear++;
+                  tmpInt = dtYear - 2000;
+                  if((tmpInt % 4) == 0)
+                    febLen = 29;
+                  else
+                    febLen = 28;
+                  dateTimeStr[5] = '0';
+                  dateTimeStr[6] = '1';
+
+                  divResult   = DIV(tmpInt,100);
+                  tmpInt100   = divResult.quot;
+                  tmpInt      = divResult.rem;
+
+                  divResult   = DIV(tmpInt,10);
+                  tmpInt10    = divResult.quot;
+                  tmpInt1     = divResult.rem;
+
+                  dateTimeStr[1] = (char) (tmpInt100 | 0x30);
+                  dateTimeStr[2] = (char) (tmpInt10  | 0x30);
+                  dateTimeStr[3] = (char) (tmpInt1   | 0x30);
+                }
+                else
+                {
+                  divResult      = DIV(dtMonth,10);
+                  dateTimeStr[5] = (char) (divResult.quot | 0x30);
+                  dateTimeStr[6] = (char) (divResult.rem  | 0x30);
+                }
+              }
+              else
+              {
+                divResult      = DIV(dtDay,10);
+                dateTimeStr[8] = (char) (divResult.quot | 0x30);
+                dateTimeStr[9] = (char) (divResult.rem  | 0x30);
+              }
+            }
+            else
+            {
+              divResult       = DIV(dtHour,10);
+              dateTimeStr[11] = (char) (divResult.quot | 0x30);
+              dateTimeStr[12] = (char) (divResult.rem  | 0x30);
+            }
+          }
+          else
+          {
+            divResult       = DIV(dtMin,10);
+            dateTimeStr[14] = (char) (divResult.quot | 0x30);
+            dateTimeStr[15] = (char) (divResult.rem  | 0x30);
+          }
+        }
+        else
+        {
+          divResult     = DIV(dtSec,10);
+          dateTimeStr[17] = (char) (divResult.quot | 0x30);
+          dateTimeStr[18] = (char) (divResult.rem  | 0x30);
+        }
+      }
+      else
+      {
+        divResult   = DIV(dtmSec,100);
+        tmpInt100   = divResult.quot;
+        tmpInt      = divResult.rem;
+
+        divResult   = DIV(tmpInt,10);
+        tmpInt10    = divResult.quot;
+        tmpInt1     = divResult.rem;
+
+        dateTimeStr[20] = (char) (tmpInt100 | 0x30);
+        dateTimeStr[21] = (char) (tmpInt10  | 0x30);
+        dateTimeStr[22] = (char) (tmpInt1   | 0x30);
+      }
+
+    }
+
+    if(firstLoop == false)
+    {
+      backgroundMicros = loopStartMicros - loopEndMicros;
+      if(backgroundMicros > backgroundMaxMicros)
+        backgroundMaxMicros = backgroundMicros;
+      if(backgroundMicros < backgroundMinMicros)
+        backgroundMinMicros = backgroundMicros;
+      periodMicros = loopStartMicros - lastStartMicros;
+      if(periodMicros > periodMaxMicros)
+        periodMaxMicros = periodMicros;
+      periodSumMicros += periodMicros;
+      if(periodMicros < periodMinMicros)
+        periodMinMicros = periodMicros;
+      if(periodMicros > PeriodMinTime)
+      {
+        periodFailAlarm = true;
+        periodFailCount++;
+      }
+
+      divResult = DIV(periodMicros, 1000);
+      if(divResult.quot > 0)
+      {
+        if(divResult.quot >= LoopScreeningGrades)
+          ++loopScreening[LoopScreeningGrades - 1];
+        else
+          ++loopScreening[divResult.quot -1];
+      }
+    } // if()
+
+    lastStartMicros = loopStartMicros;
+
+  }
+
+
+  unsigned int LoopCheck::done()
+  {
+    return(SYSMICSEC - loopStartMicros);
+  }
+
+  void LoopCheck::end()
+  {
+    loopEndMicros = SYSMICSEC;
+    loopMicros = loopEndMicros - loopStartMicros;
+    if(loopMicros > loopMaxMicros)
+      loopMaxMicros = loopMicros;
+    if(loopMicros < loopMinMicros)
+      loopMinMicros = loopMicros;
+
+    if(firstLoop == false)
+    {
+      loopSumMicros += loopMicros;
+      backgroundSumMicros += backgroundMicros;
+      calcAvgCounter++;
+      if(calcAvgCounter == CalcAverageDepth)
+      {
+        loopAvgMicros = loopSumMicros / CalcAverageDepth;
+        backgroundAvgMicros = backgroundSumMicros / CalcAverageDepth;
+        periodAvgMicros = periodSumMicros / CalcAverageDepth;
+        calcAvgCounter = 0;
+        loopSumMicros = 0;
+        backgroundSumMicros = 0;
+        periodSumMicros = 0;
+      }
+    }
+    else
+    {
+      loopAvgMicros = loopMicros;
+      backgroundAvgMicros = backgroundMicros;
+    }
+
+    loopCounter++;
+    firstLoop = false;
+    taskHappened = false;
+  }
+
+  bool LoopCheck::timerMicro
+    (int taskIdx, unsigned long repeatTime, unsigned int repetitions, unsigned long delay)
+  {
+    TimerTask     *ctrlPtr;
+    unsigned long calcMics;
+
+    // Test the limit of enabled timers
+    //
+    if(taskIdx < 0) return(false);
+    if(taskIdx >= NrOfTimerTasks) return(false);
+
+    // Get the reference to timer data for the selected timer
+    //
+    ctrlPtr = &timerTaskList[taskIdx];
+
+    // If the timer task has finished, we are ready here
+    //
+    if(ctrlPtr->finished == true) return(false);
+
+    // If it is the first run (initialisation 1)
+    //
+    if(ctrlPtr->firstRun == true)
+    {
+      ctrlPtr->firstRun     = false;
+      ctrlPtr->repCounter   = repetitions;
+      ctrlPtr->delayCounter = delay;
+    }
+
+    // If counting is not started yet (initialisation 2)
+    //
+    if(ctrlPtr->counterStarted == false)
+    {
+      ctrlPtr->startCount = loopStartMicros;
+      ctrlPtr->counterStarted = true;
+      return(false);
+    }
+
+    // If another count task has happened in this loop, we have to wait
+    //
+    if(taskHappened == true) return(false);
+
+    // Calculate the number of microseconds since the start of the counter
+    //
+    calcMics = loopStartMicros - ctrlPtr->startCount;
+    ctrlPtr->ticks = calcMics;
+
+    // If there is a delay, wait the delay time
+    //
+    if(ctrlPtr->delayCounter > 0)
+    {
+      if(calcMics < ctrlPtr->delayCounter)
+        return(false);
+      else
+      {
+        ctrlPtr->delayCounter = 0;                // delay finished
+        ctrlPtr->startCount = loopStartMicros;    // reset counter
+      }
+      return(false);
+    }
+
+    // There is no delay (or delay is finished)
+    // If repeatTime is not passed, leave with FALSE
+    //
+    if(calcMics < repeatTime)
+    {
+      return(false);
+    }
+
+    // One counter period finished
+    //
+    taskHappened            = true;               // disable other timers in this loop
+    ctrlPtr->counterStarted = false;              // prepare resetting the counter
+    ctrlPtr->runCounter++;                        // count the timer events
+
+    // If the number of periods is limited finish in time
+    //
+    if(ctrlPtr->repCounter > 0)
+    {
+      ctrlPtr->repCounter--;
+      if(ctrlPtr->repCounter == 0)
+        ctrlPtr->finished = true;
+    }
+
+    return(true);
+  }
+
+  bool LoopCheck::timerMicro
+    (int taskIdx, unsigned long repeatTime, unsigned int repetitions)
+  {
+    return(timerMicro(taskIdx, repeatTime, repetitions, 0));
+  }
+
+
+  bool LoopCheck::timerMilli
+    (int taskIdx, unsigned long repeatTime, unsigned int repetitions, unsigned long delay)
+  {
+    return(timerMicro(taskIdx, repeatTime * 1000, repetitions, delay * 1000));
+  }
+
+  bool LoopCheck::timerMilli
+    (int taskIdx, unsigned long repeatTime, unsigned int repetitions)
+  {
+    return(timerMicro(taskIdx,repeatTime * 1000,repetitions,0));
+  }
+
+  bool LoopCheck::once(int taskIdx)
+  {
+    if(taskIdx < 0) return(false);
+    if(taskIdx >= NrOfOnceTasks) return(false);
+
+    if(onceTaskList[taskIdx].finished == true) return(false);
+    onceTaskList[taskIdx].finished = true;
+    return(true);
+  }
+
+  bool LoopCheck::once(int taskIdx, unsigned int nrOfLoops)
+  {
+    if(taskIdx < 0) return(false);
+    if(taskIdx >= NrOfOnceTasks) return(false);
+
+    if(onceTaskList[taskIdx].finished == true) return(false);
+    if(nrOfLoops <= 1)
+    {
+      onceTaskList[taskIdx].finished = true;
+      return(true);
+    }
+
+    if(onceTaskList[taskIdx].firstRun == true)
+    {
+      onceTaskList[taskIdx].firstRun = false;
+      onceTaskList[taskIdx].waitCounter = nrOfLoops;
+    }
+
+    onceTaskList[taskIdx].waitCounter--;
+    if(onceTaskList[taskIdx].waitCounter > 0)
+      return(false);
+
+    onceTaskList[taskIdx].finished = true;
+    return(true);
+  }
+
+  bool LoopCheck::onceDelayed(int taskIdx, unsigned long delay)
+  {
+    if(taskIdx < 0) return(false);
+    if(taskIdx >= NrOfOnceTasks) return(false);
+
+    if(onceTaskList[taskIdx].finished == true) return(false);
+
+    if(onceTaskList[taskIdx].firstRun == true)
+    {
+      onceTaskList[taskIdx].firstRun = false;
+      onceTaskList[taskIdx].startCount = loopStartMicros;
+    }
+
+    if(loopStartMicros - onceTaskList[taskIdx].startCount < delay)
+      return(false);
+
+    onceTaskList[taskIdx].finished = true;
+    return(true);
+  }
+
+  bool LoopCheck::toggle(int taskIdx)
+  {
+    bool    toggleBit;
+
+    if(taskIdx < 0) return(false);
+    if(taskIdx >= NrOfToggleTasks) return(false);
+    toggleBit = toggleTaskList[taskIdx];
+    toggleTaskList[taskIdx] = !toggleBit;
+    return(toggleBit);
+  }
+
+  unsigned long LoopCheck::timerCycle(int taskIdx)
+  {
+    if(taskIdx < 0) return(0);
+    if(taskIdx >= NrOfTimerTasks) return(0);
+    return(timerTaskList[taskIdx].runCounter);
+  }
+
+  bool LoopCheck::timerCycleMod(int taskIdx, int modulo)
+  {
+    div_t divResult;
+
+    if(taskIdx < 0) return(0);
+    if(taskIdx >= NrOfTimerTasks) return(0);
+    divResult   = DIV(timerTaskList[taskIdx].runCounter,modulo);
+    if(divResult.rem == 0)
+      return(true);
+    else
+      return(false);
+  }
+
+  unsigned long LoopCheck::tick(int taskIdx)
+  {
+    if(taskIdx < 0) return(0);
+    if(taskIdx >= NrOfTimerTasks) return(0);
+    return(timerTaskList[taskIdx].ticks);
+  }
+
+  unsigned long LoopCheck::operationTime(OpHourMeter *opHourMeter)
+  {
+    opHourMeter->Milliseconds    = msec;
+    opHourMeter->Seconds         = sec;
+    opHourMeter->Minutes         = min;
+    opHourMeter->Hours           = hour;
+    opHourMeter->Days            = day;
+    opHourMeter->Years           = year;
+    return(loopStartMicros);
+  }
+
+  unsigned long LoopCheck::getStatistics(LoopStatistics *statistics)
+  {
+    statistics->loopTime    =   (unsigned int) loopMicros;
+    statistics->loopMaxTime =   (unsigned int) loopMaxMicros;
+    statistics->loopMinTime =   (unsigned int) loopMinMicros;
+    statistics->loopAvgTime =   (unsigned int) loopAvgMicros;
+
+    statistics->bgTime      =   (unsigned int) backgroundMicros;
+    statistics->bgMaxTime   =   (unsigned int) backgroundMaxMicros;
+    statistics->bgMinTime   =   (unsigned int) backgroundMinMicros;
+    statistics->bgAvgTime   =   (unsigned int) backgroundAvgMicros;
+
+    statistics->alarmCount  =   periodFailCount;
+    statistics->periodAlarm =   periodFailAlarm;
+    periodFailAlarm = false;
+
+    statistics->loopPeriod  =   periodMicros;
+    statistics->maxPeriod   =   periodMaxMicros;
+    statistics->minPeriod   =   periodMinMicros;
+    statistics->avgPeriod   =   periodAvgMicros;
+
+    for (int i = 0; i < LoopScreeningGrades; i++)
+      statistics->rtScreening[i] = loopScreening[i];
+
+    return(loopCounter);
+  }
+
+  void LoopCheck::resetStatistics()
+  {
+    backgroundMicros        = 0;
+    loopMicros              = 0;
+
+    backgroundMaxMicros     = 0;
+    backgroundMinMicros     = (unsigned long) -1;
+    backgroundAvgMicros     = 0;
+
+    loopMaxMicros           = 0;
+    loopMinMicros           = (unsigned long) -1;
+    loopAvgMicros           = 0;
+    loopCounter             = 0;
+
+    periodFailCount         = 0;
+    periodMaxMicros         = 0;
+    periodMinMicros         = (unsigned int) -1;
+    periodAvgMicros         = 0;
+    periodMicros            = 0;
+    periodFailAlarm         = false;
+
+    for (int i = 0; i < LoopScreeningGrades; i++)
+      loopScreening[i] = 0;
+
+    calcAvgCounter          = 0;
+  }
+
+  bool LoopCheck::setDateTime(const char *dtStr)
+  {
+    int tmpInt;
+
+     if(strlen(dtStr) < 23) return(false);
+     strcpy(dateTimeStr,dtStr);
+     dtYear   = (dateTimeStr[0]  & 0x0F) * 1000 +
+                (dateTimeStr[1]  & 0x0F) * 100  +
+                (dateTimeStr[2]  & 0x0F) * 10   +
+                (dateTimeStr[3]  & 0x0F);
+
+     tmpInt = dtYear - 2000;
+     if((tmpInt % 4) == 0)
+       febLen = 29;
+     else
+       febLen = 28;
+
+
+     dtMonth  = (dateTimeStr[5]  & 0x0F) * 10   +
+                (dateTimeStr[6]  & 0x0F);
+
+     dtDay    = (dateTimeStr[8]  & 0x0F) * 10   +
+                (dateTimeStr[9]  & 0x0F);
+
+     dtHour   = (dateTimeStr[11] & 0x0F) * 10   +
+                (dateTimeStr[12] & 0x0F);
+
+     dtMin    = (dateTimeStr[14] & 0x0F) * 10   +
+                (dateTimeStr[15] & 0x0F);
+
+     dtSec    = (dateTimeStr[17] & 0x0F) * 10   +
+                (dateTimeStr[18] & 0x0F);
+
+     dtmSec   = (dateTimeStr[20] & 0x0F) * 10   +
+                (dateTimeStr[21] & 0x0F) * 10   +
+                (dateTimeStr[22] & 0x0F);
+
+     return(true);
+  }
+
+  bool LoopCheck::setDateTime(lcDateTime dt)
+  {
+    div_t   divResult;
+    int     tmpInt;
+
+    dtYear    = dt.Year;
+
+    tmpInt = dtYear - 2000;
+    if((tmpInt % 4) == 0)
+      febLen = 29;
+    else
+      febLen = 28;
+
+
+    divResult = DIV(dtYear,1000);
+    dateTimeStr[0] = (char) (0x30 + divResult.quot);
+
+    divResult = DIV(divResult.rem,100);
+    dateTimeStr[1] = (char) (0x30 + divResult.quot);
+
+    divResult = DIV(divResult.rem,10);
+    dateTimeStr[2] = (char) (0x30 + divResult.quot);
+    dateTimeStr[3] = (char) (0x30 + divResult.rem);
+
+    dtMonth   = dt.Month;
+    divResult = DIV(dtMonth,10);
+    dateTimeStr[5] = (char) (0x30 + divResult.quot);
+    dateTimeStr[6] = (char) (0x30 + divResult.rem);
+
+    dtDay     = dt.Day;
+    divResult = DIV(dtDay,10);
+    dateTimeStr[8] = (char) (0x30 + divResult.quot);
+    dateTimeStr[9] = (char) (0x30 + divResult.rem);
+
+    dtHour    = dt.Hour;
+    divResult = DIV(dtHour,10);
+    dateTimeStr[11] = (char) (0x30 + divResult.quot);
+    dateTimeStr[12] = (char) (0x30 + divResult.rem);
+
+    dtMin     = dt.Minute;
+    divResult = DIV(dtMin,10);
+    dateTimeStr[14] = (char) (0x30 + divResult.quot);
+    dateTimeStr[15] = (char) (0x30 + divResult.rem);
+
+    dtSec     = dt.Second;
+    divResult = DIV(dtSec,10);
+    dateTimeStr[17] = (char) (0x30 + divResult.quot);
+    dateTimeStr[18] = (char) (0x30 + divResult.rem);
+
+    dtmSec    = dt.Millisecond;
+    divResult = DIV(dtmSec, 100);
+    dateTimeStr[20] = (char) (0x30 + divResult.quot);
+    divResult = DIV(divResult.rem, 10);
+    dateTimeStr[21] = (char) (0x30 + divResult.quot);
+    dateTimeStr[22] = (char) (0x30 + divResult.rem);
+
+    return(true);
+  }
+
+  bool LoopCheck::getDateTime(lcDateTime *dt)
+  {
+    dt->Year        = dtYear;
+    dt->Month       = dtMonth;
+    dt->Day         = dtDay;
+    dt->Hour        = dtHour;
+    dt->Minute      = dtMin;
+    dt->Second      = dtSec;
+    dt->Millisecond = dtmSec;
+    return(true);
+  }
+
+  const char * LoopCheck::refDateTime()
+  {
+    return(dateTimeStr);
+  }
+
+  unsigned long LoopCheck::locMicros()
+  {
+#ifdef smnSimLinux
+    struct timespec clockTime;
+    unsigned long retv;
+
+    clock_gettime(CLOCK_MONOTONIC, &clockTime);
+    retv = clockTime.tv_nsec / 1000;
+    return(retv);
+#endif
+
+#ifdef smnSimWindows
+    LARGE_INTEGER countValue, frequency, result;
+
+    QueryPerformanceCounter(&countValue);
+    QueryPerformanceFrequency(&frequency);
+
+    result.QuadPart = (countValue.QuadPart * 1000000) / frequency.QuadPart;
+    return((unsigned long) result.QuadPart);
+#endif
+
+#ifdef smnSloeber
+    return(micros());
+#endif
+  }
+
+#ifdef smnESP8266
+  div_t LoopCheck::locDiv(int numer, int denom)
+  {
+    div_t retv;
+
+    retv.quot = numer / denom;
+    retv.rem  = numer % denom;
+
+    return(retv);
+  }
+#endif
+
+  void LoopCheck::startTimeMeasure()
+  {
+    measureTimeSet = SYSMICSEC;
+  }
+
+  unsigned long LoopCheck::getTimeMeasure()
+  {
+    return(SYSMICSEC - measureTimeSet);
+  }
+
+  unsigned long LoopCheck::getRuntime()
+  {
+    return(measureRuntime);
+  }
+
+  void LoopCheck::hexAsc(char * dest, byte val)
+  {
+    char cv;
+
+    cv = val >> 4;
+    if(cv < 10)
+      cv += 0x30;
+    else
+      cv += 0x37;
+    dest[0] = cv;
+
+    cv = val & 0x0F;
+    if(cv < 10)
+      cv += 0x30;
+    else
+      cv += 0x37;
+    dest[1] = cv;
+
+    dest[2] = '\0';
+  }
+
+
+  // -------------------------------------------------------------------------
+  // Debug-Funktionen
+  // -------------------------------------------------------------------------
+  //
+#ifdef smnLoopCheckDebug
+
+  void LoopCheck::dbgGetStatistics(char *buffer, int idxItem)
+  {
+    switch(idxItem)
+    {
+      case 0:
+        sprintf(buffer,"lT=%d, lMaxT=%d, lMinT=%d, lAvgT=%d",
+            loopMicros,loopMaxMicros,loopMinMicros,loopAvgMicros);
+        break;
+
+      case 1:
+        sprintf(buffer,"bT=%d, bMaxT=%d, bMinT=%d, bAvgT=%d",
+            backgroundMicros,backgroundMaxMicros,backgroundMinMicros,backgroundAvgMicros);
+        break;
+
+      case 2:
+        sprintf(buffer,"rtAlCnt=%d, lCnt=%d, Scr=%d,%d,%d,%d,%d,%d",
+            periodFailCount, loopCounter, loopScreening[0],loopScreening[1],loopScreening[2],loopScreening[3],loopScreening[4],loopScreening[5]);
+        break;
+    }
+  }
+
+#endif
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/LoopCheck/LoopCheck.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/LoopCheck/LoopCheck.h
new file mode 100644
index 0000000000000000000000000000000000000000..9588f3e56dc3ebe52890b772533793e533aad05d
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/LoopCheck/LoopCheck.h
@@ -0,0 +1,343 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   LoopCheck.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (siehe Wikipedia: Creative Commons)
+//
+#ifndef _LoopCheck_h
+#define _LoopCheck_h
+//-----------------------------------------------------------------------------
+
+#define PeriodMinTime       5000
+// Wenn der Aufrufzyklus der Loop diese Zeit (in Mikrosekunden) überschreitet,
+// dann wird ein Alarmbit gesetzt und ein Alarmzähler inkrementiert
+
+#ifndef LoopScreeningGrades
+  #define LoopScreeningGrades 6
+#endif
+
+#define NrOfTimerTasks      10
+
+#define lcTimer0  0
+#define lcTimer1  1
+#define lcTimer2  2
+#define lcTimer3  3
+#define lcTimer4  4
+#define lcTimer5  5
+#define lcTimer6  6
+#define lcTimer7  7
+#define lcTimer8  8
+#define lcTimer9  9
+
+
+#define NrOfOnceTasks       4
+
+#define lcOnce0  0
+#define lcOnce1  1
+#define lcOnce2  2
+#define lcOnce3  3
+
+#define NrOfToggleTasks     4
+
+#define lcToggle0  0
+#define lcToggle1  1
+#define lcToggle2  2
+#define lcToggle3  3
+
+#define CalcAverageDepth    32
+
+#ifdef UseGithubPath
+  #include "../environment/environment.h"
+#else
+  #include "environment.h"
+#endif
+
+#if defined(smnSimLinux) || defined(smnSimWindows)
+  #include <stdlib.h>
+  #include <string.h>
+  #include <time.h>
+  #define SYSMICSEC locMicros()
+#endif
+
+#ifdef smnSimWindows
+#include <Windows.h>
+#endif
+
+#ifdef smnSloeber
+  #include "Arduino.h"
+  #define SYSMICSEC    micros()
+#endif
+
+#ifdef smnESP8266
+  #define DIV(x,y)    locDiv(x,y)
+#else
+  #define DIV(x,y)    div(x,y)
+#endif
+
+typedef struct _OpHourMeter
+{
+  int   Years;
+  int   Days;
+  int   Hours;
+  int   Minutes;
+  int   Seconds;
+  int   Milliseconds;
+} OpHourMeter;
+
+typedef struct _lcDateTime
+{
+  int   Year;
+  int   Month;
+  int   Day;
+  int   Hour;
+  int   Minute;
+  int   Second;
+  int   Millisecond;
+} lcDateTime;
+
+typedef struct _LoopStatistics
+{
+  unsigned int  loopTime;       // Schleifenzeit in Mikrosekunden
+  unsigned int  loopMaxTime;    // Maximale Schleifenzeit
+  unsigned int  loopMinTime;    // Minimale Schleifenzeit
+  unsigned int  loopAvgTime;    // Mittlere Schleifenzeit
+
+  unsigned int  bgTime;         // Zeit außerhalb der Schleife
+  unsigned int  bgMaxTime;      // Maximale Außenzeit
+  unsigned int  bgMinTime;      // Minimale Außenzeit
+  unsigned int  bgAvgTime;      // Mittlere Außenzeit
+
+  unsigned int  loopPeriod;     // Zeit zwischen loop-Aufrufen
+  unsigned int  maxPeriod;      // Maximale Aufrufdistanz
+  unsigned int  minPeriod;      // Minimale Aufrufdistanz
+  unsigned int  avgPeriod;      // Mittlere Aufrufdistanz
+
+  bool          periodAlarm;    // Aufrufdistanz > PeriodMinTime
+  unsigned int  alarmCount;     // Anzahl der Überschreitungen
+
+  unsigned int  rtScreening[LoopScreeningGrades];
+  // Echtzeitüberwachung (Klassierung der ms Überschreitungen)
+} LoopStatistics;
+
+
+// ---------------------------------------------------------------------------
+// class LoopCheck
+// ---------------------------------------------------------------------------
+//
+class LoopCheck
+{
+  // -------------------------------------------------------------------------
+  // Klassenspezifische Datentypen
+  // -------------------------------------------------------------------------
+  //
+  typedef struct _TimerTask
+  {
+    bool            counterStarted;
+    bool            finished;
+    bool            firstRun;
+    unsigned long   startCount;
+    unsigned long   runCounter;
+    unsigned long   delayCounter;
+    unsigned long   ticks;
+    unsigned int    repCounter;
+  } TimerTask;
+
+  typedef struct _OnceTask
+  {
+    bool            finished;
+    bool            firstRun;
+    unsigned int    waitCounter;
+    unsigned long   startCount;
+  } OnceTask;
+
+private:
+  // -------------------------------------------------------------------------
+  // Lokale Variablen
+  // -------------------------------------------------------------------------
+  //
+  unsigned long checkStartMicros;       // Zeit des ersten Aufrufs von begin()
+
+  unsigned long backgroundMicros;       // Zeit, die außerhalb von loop()
+                                        // verstrichen ist (in Mikrosekunden)
+  unsigned long loopMicros;             // Zeit, die innerhalb von loop()
+                                        // verstrichen ist (in Mikrosekunden)
+  unsigned long loopStartMicros;        // Loop-Startzeit (us seit CPU-Start)
+  unsigned long lastClockMicros;
+  unsigned long lastStartMicros;
+  unsigned long lastRestMicros;
+
+  unsigned long loopEndMicros;          // Loop-Endezeit (us seit CPU-Start)
+  unsigned long clockCycleMicros;       // Abstand zwischen zwei clock ticks
+  unsigned long mainStartMicros;        // Zählerstand bei Programmstart
+
+  unsigned long backgroundMaxMicros;    // Maximale Zeit außerhalb loop()
+  unsigned long backgroundMinMicros;    // Minimale Zeit außerhalb loop()
+  unsigned long backgroundAvgMicros;    // Mittlere Zeit außerhal loop() {32}
+  unsigned long backgroundSumMicros;    // Summe für Mittelwertberechnung
+
+  unsigned long loopMaxMicros;          // Maximale Zeit innerhalb loop()
+  unsigned long loopMinMicros;          // Minimale Zeit innerhalb loop()
+  unsigned long loopAvgMicros;          // Mittlere Zeit innerhalb loop()
+  unsigned long loopSumMicros;          // Summe für Mittelwertberechnung
+
+  unsigned long loopCounter;            // Anzahl der loop()-Durchläufe
+
+  unsigned int  loopScreening[LoopScreeningGrades];
+
+  int           calcAvgCounter;         // Zähler für die Mittelwertbildung
+  bool          firstLoop;              // Spezielle Kennzeichnung erste loop()
+  bool          taskHappened;           // Kennzeichnung: Es lief ein LoopTask
+
+  TimerTask     timerTaskList[NrOfTimerTasks];  // Steuerung der zyklischen
+                                                // Tasks (Timer-Ersatz in loop())
+  OnceTask      onceTaskList[NrOfOnceTasks];
+  bool          toggleTaskList[NrOfToggleTasks];
+
+  int           year;               // Betriebsstundenzähler gesamt
+  int           day;
+  int           hour;
+  int           min;
+  int           sec;
+  int           msec;
+  bool          toggleMilli;
+
+  int           dtYear;             // Zeit / Uhr
+  int           dtMonth;
+  int           dtDay;
+  int           dtHour;
+  int           dtMin;
+  int           dtSec;
+  int           dtmSec;
+  int           febLen;
+  char          dateTimeStr[30];
+
+  unsigned int  periodMicros;           // Zeit zwischen zwei loop-Aufrufen
+  unsigned int  periodMinMicros;
+  unsigned int  periodMaxMicros;
+  unsigned int  periodAvgMicros;
+  unsigned int  periodSumMicros;
+
+  bool          periodFailAlarm;        // periodMicros > Millisekunde
+  unsigned int  periodFailCount;        // Anzahl der Überschreitungen
+
+  unsigned long measureTimeSet;         // Mikrosekunden-Offset Zeitmessung
+
+  unsigned long measureRuntime;         // Laufzeit seit Start in Sekunden
+
+private:
+  // -------------------------------------------------------------------------
+  // Lokale Funktionen
+  // -------------------------------------------------------------------------
+  //
+  void initTasks();
+  void initStatistics();
+  void initClock();
+  unsigned long locMicros();
+#ifdef smnESP8266
+  div_t locDiv(int numer, int denom);
+#endif
+
+public:
+  // -------------------------------------------------------------------------
+  // Konstruktoren und Initialisierungen
+  // -------------------------------------------------------------------------
+  //
+  LoopCheck();
+
+  // -------------------------------------------------------------------------
+  // Anwenderfunktionen
+  // -------------------------------------------------------------------------
+  //
+  void begin();     // Diese Funktion muss am Anfang der Schleife aufgerufen
+                    // werden.
+
+  unsigned int done();  // Diese Funktion kann vor dem Aufruf von end()
+                        // genutzt werden und liefert die Laufzeit bis dahin
+
+  void end();       // Diese Funktion muss am Ende der Schleife aufgerufen
+                    // werden.
+
+  bool timerMicro(int taskIdx, unsigned long repeatTime, unsigned int repetitions);
+  bool timerMicro(int taskIdx, unsigned long repeatTime, unsigned int repetitions, unsigned long delay);
+  // Diese Funktion muss als Bedingung (if) aufgerufen werden, um den
+  // nachfolgenden Block {} mit der Wiederholzeit <repeatTime> auszuführen
+  // Für jede Taskschleife muss ein anderer Index <taskIdx> aus dem Bereich
+  // 0 <= taskIdx < MaxNrOfLoopTasks angegeben werden.
+  // Mit <repetitions> wird angegeben, wie oft der Durchlauf überhaupt erfolgt.
+  // Der Wert 0 gibt an, dass der Task für immer läuft
+
+  bool timerMilli(int taskIdx, unsigned long repeatTime, unsigned int repetitions);
+  bool timerMilli(int taskIdx, unsigned long repeatTime, unsigned int repetitions, unsigned long delay);
+
+  bool once(int taskIdx);
+  // Diese Funktion liefert nur einmal den Wert <true>
+
+  bool once(int taskIdx, unsigned int nrOfLoops);
+  // Diese Funktion liefert nur einmal den Wert <true>
+  // nach Ablauf von nrOfLoops Aufrufen
+
+  bool onceDelayed(int taskIdx, unsigned long delay);
+  // Diese Funktion liefert nur einmal den Wert <true>
+  // nach Ablauf von <delay> Mikrosekunden
+
+  bool toggle(int taskIdx);
+  // Diese Funktion liefert abwechselnd die Werte <true> oder <false>
+
+  unsigned long timerCycle(int taskIdx);
+  // Rückgabe des aktuellen Timerablaufes (startet ab 0).
+
+  bool timerCycleMod(int taskIdx, int modulo);
+  // Liefert alle <modulo> Timerabläufe den Wert <true>
+
+  unsigned long tick(int taskIdx);
+  // Rückgabe des aktuellen Zählwertes in Mikrosekunden
+
+  unsigned long operationTime(OpHourMeter *opHourMeter);
+  // Die Zeit ab Start der CPU
+
+  unsigned long getStatistics(LoopStatistics *statistics);
+  // Statistik über Ablaufzeiten
+
+  void resetStatistics();
+  // Rücksetzen der Statistikdaten
+
+  bool setDateTime(const char *dtStr);
+  // Setzen der Uhr über standardisierten String
+
+  bool setDateTime(lcDateTime dt);
+  // Setzen der Uhr über lokal definierte Struktur
+
+  bool getDateTime(lcDateTime *dt);
+  // Abfragen der Uhr über lokal definierte Struktur
+
+  const char * refDateTime();
+  // Zeiger auf Datum/Uhrzeit holen
+
+  void startTimeMeasure();
+  // Zeitmessung starten
+
+  unsigned long getTimeMeasure();
+  // Zeitmesswert holen
+
+  unsigned long getRuntime();
+  // Laufzeit in Sekunden
+
+  void hexAsc(char * dest, byte val);
+  // Umwandlung byte in Hex-ASCII
+
+  // -------------------------------------------------------------------------
+  // Debug-Funtionen
+  // -------------------------------------------------------------------------
+  //
+
+#ifdef smnLoopCheckDebug
+  void dbgGetStatistics(char *buffer, int idxItem);
+#endif
+
+};
+
+//-----------------------------------------------------------------------------
+#endif
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/LoopCheck/ReadMe.md b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/LoopCheck/ReadMe.md
new file mode 100644
index 0000000000000000000000000000000000000000..7701d9d3347942f8c88807f2f8342bade47ace46
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/LoopCheck/ReadMe.md
@@ -0,0 +1,20 @@
+# Tools for cyclic called procedures
+Arduino Sketches are build on two basic functions. 
+*void setup()* is called once when the CPU is reset and programmers place their initialisation code here.
+*void loop()* is called in an endless loop, i.e. a cyclic entered function. 
+But the cycle time is not determined, it depends on the speed of the CPU and the used resources.
+Many examples for Arduino use the function *delay(milliseconds)* to organise a kind of timing. 
+But this function is really freezing your program for the given number of milliseconds.
+Using a real timer is a good solution, but some CPUs have only less timers 
+and they sometimes are already used for some libraries.
+
+The tools presented with LoopCheck-library give You the features of (many) timers inside *loop()* 
+based on the Arduino-function *micros()* which is called with the macro SYSMICSEC, 
+defined in *LoopCheck.h*.
+
+You will find, that there is another file included: *environment.h*, which you can find here:
+https://github.com/RobertPatzke/homeautomation/blob/developer/libraries/environment/environment.h
+*environment.h* defines the IDE you are using, the CPU and specific development boards. 
+Code is in several parts conditional, depending on the definitions you make in *environment.h*.
+You will see, that this library is not fixed to Arduino, it may be used for any environment
+where cyclic called functions happen.
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/LoopCheck/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/LoopCheck/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..3af69246689b448266e74521f4ff1d792450456f
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/LoopCheck/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "LoopCheck",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/MidiNotes/MidiNotes.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/MidiNotes/MidiNotes.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ec91464f7411b1f25f33b3b536faf3b8dc1e8dcd
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/MidiNotes/MidiNotes.cpp
@@ -0,0 +1,405 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   Midi.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   27. November 2021
+//
+// Der Inhalt dieser Datei sind Festlegungen für steuerbare Midi-Controller.
+//
+//
+
+#include "MidiNotes.h"
+
+// ----------------------------------------------------------------------------
+// Initialisierungen
+// ----------------------------------------------------------------------------
+//
+
+void MidiNotes::begin(int inBpm, NoteDiv inRes, int inMidiCycle, IntrfBuf *inCRB)
+{
+  dword       stdNoteMicroTick;
+
+  crb = inCRB;                        // Zeiger auf Ringpuffer
+  midiCycle = inMidiCycle;            // Zuklus der ZM in Mikrosekunden
+  bpm = inBpm;                        // Beats Per Minute (Metronom)
+  stdNoteMicroTick = 60000000 / bpm;  // Viertelnotenlänge in Mikrosekunden
+  stdNoteCount = stdNoteMicroTick / inMidiCycle;  // "  in Zyklen der ZM
+  stdNoteTick = 60000 / bpm;          // Viertelnotenlänge in Millisekunden
+  minNoteTick = stdNoteTick / inRes;  // Zu erwartende kürzeste Note (ms)
+
+  typeList[nti0].length   = 8 * stdNoteCount;
+  setNoteType(nti0);
+
+  typeList[nti1].length   = 4 * stdNoteCount;
+  setNoteType(nti1);
+
+  typeList[nti2p].length  = 3 * stdNoteCount;
+  setNoteType(nti2p);
+
+  typeList[nti2].length   = 2 * stdNoteCount;
+  setNoteType(nti2);
+
+  typeList[nti4p].length  = stdNoteCount + stdNoteCount / 2;
+  setNoteType(nti4p);
+
+  // Standard-Note = Viertelnote (
+  typeList[nti4].length   = stdNoteCount;
+  setNoteType(nti4);
+
+  typeList[nti8].length   = stdNoteCount / 2;
+  setNoteType(nti8);
+
+  typeList[nti8p].length  = stdNoteCount / 2 + stdNoteCount / 4;
+  setNoteType(nti8p);
+
+  typeList[nti16].length  = stdNoteCount / 4;
+  setNoteType(nti16);
+
+  typeList[nti16p].length = stdNoteCount / 4 + stdNoteCount / 8;
+  setNoteType(nti16p);
+
+  typeList[nti32].length  = stdNoteCount / 8;
+  setNoteType(nti32);
+
+  typeList[nti32p].length = stdNoteCount / 8 + stdNoteCount / 16;
+  setNoteType(nti32p);
+
+  typeList[nti64].length  = stdNoteCount / 16;
+  setNoteType(nti64);
+
+  typeList[nti64p].length = stdNoteCount / 16 + stdNoteCount / 32;
+  setNoteType(nti64p);
+
+  opMode = momIdle;
+  setChannel(1);
+  stopRun = false;
+  stoppedRun = false;
+  next(smInit);
+}
+
+
+// ----------------------------------------------------------------------------
+// Konfiguration
+// ----------------------------------------------------------------------------
+//
+void MidiNotes::setNoteType(NoteTypeIdx nt)
+{
+  NoteTypePtr typePtr;
+
+  typePtr = &typeList[nt];
+  typePtr->attack   = 0;
+  typePtr->decay    = 0;
+  typePtr->sustain  = typePtr->length;
+  typePtr->release  = 0;
+  typePtr->pause    = (typePtr->length * 20) / 100;
+
+  typePtr->deltaAttack    = 0;
+  typePtr->deltaDecay     = 0;
+  typePtr->percentSustain = 70;
+  typePtr->deltaRelease   = 0;
+}
+
+
+void MidiNotes::setNoteType(NoteTypeIdx nt, byte pAttL, byte pDecL, byte pSusL, byte pRelL,
+                            byte pPauL, byte dAtt, byte dDec, byte pSusV, byte dRel)
+{
+  NoteTypePtr typePtr;
+
+  typePtr = &typeList[nt];
+  typePtr->attack   = (typePtr->length * pAttL) / 100;
+  typePtr->decay    = (typePtr->length * pDecL) / 100;
+  typePtr->sustain  = (typePtr->length * pSusL) / 100;
+  typePtr->release  = (typePtr->length * pRelL) / 100;
+  typePtr->pause    = (typePtr->length * pPauL) / 100;
+
+  typePtr->deltaAttack    = dAtt;
+  typePtr->deltaDecay     = dDec;
+  typePtr->percentSustain = pSusV;
+  typePtr->deltaRelease   = dRel;
+}
+
+int MidiNotes::addChordNote(NoteTypeIdx nti, byte val, byte vel)
+{
+  NotePtr notePtr;
+  int     i;
+
+  for(i = 0; i < MaxNrNoteSim; i++)
+  {
+    notePtr = &chord[i];
+    if(notePtr->mode == NoteModeEmpty)
+    {
+      notePtr->mode     = NoteModeRun;
+      notePtr->typeIdx  = nti;
+      notePtr->value    = val;
+      notePtr->veloc    = vel;
+      break;
+    }
+  }
+  return(i);
+}
+
+void MidiNotes::setChannel(int chnVal)
+{
+  if(chnVal < 1) chnVal = 1;
+  if(chnVal > 16) chnVal = 16;
+  chn = chnVal - 1;
+}
+
+// ----------------------------------------------------------------------------
+// Betrieb
+// ----------------------------------------------------------------------------
+//
+void MidiNotes::setOpMode(MidiOpMode mom)
+{
+  opMode = mom;
+}
+
+
+void MidiNotes::setChordNote(int idx, NoteTypeIdx nti, int val, int vel)
+{
+  if(idx < 0) return;
+  if(idx >= MaxNrNoteSim) return;
+
+  if(nti >= 0 && nti < ntiNr)
+    newNote[idx].typeIdx = nti;
+
+  if(val >= 0 && val <= 127)
+    newNote[idx].value = val;
+
+  if(vel >= 0 && vel <= 127)
+    newNote[idx].veloc = vel;
+
+  newNote[idx].newVal = true;
+}
+
+
+// ----------------------------------------------------------------------------
+// Steuerung, Zustandsmaschine
+// ----------------------------------------------------------------------------
+//
+
+void MidiNotes::stop()
+{
+  stopRun = true;
+}
+
+void MidiNotes::resume()
+{
+  stopRun = false;
+  stoppedRun = false;
+}
+
+void MidiNotes::run()
+{
+  runCounter++;
+  if(cycleCnt > 0) cycleCnt--;
+
+  if(nextState != NULL)
+    (this->*nextState)();
+}
+
+void MidiNotes::smInit()
+{
+  next(smIdle);
+}
+
+void MidiNotes::smIdle()
+{
+  switch(opMode)
+  {
+    case momIdle:
+      break;
+
+    case momSequence:
+      next(smNoteOn);
+      break;
+
+    case momRunDelta:
+      break;
+  }
+}
+
+void MidiNotes::smNoteOn()
+{
+  int   i, j, tIdx;
+  bool  doAttack;
+  dword attack, sustain;
+
+  if(stopRun || stoppedRun)         // Unterbrechen/Stoppen des Ablaufs
+  {                                 // vor dem Einschalten einer Note
+    stoppedRun = true;
+    return;
+  }
+
+  if(crb == NULL)                   // Ohne Ausgabepuffer in Wartezustand
+  {
+    next(smIdle);
+    return;
+  }
+
+  doAttack = false;                 // Voreinstellung kein Aufklingen
+
+  // Auslesen der Noten aus dem Akkordspeicher
+  //
+  j = 0;
+  for(i = 0; i < MaxNrNoteSim; i++)
+  {
+    notePtr = &chord[i];
+
+    if(i == 0)  // erste Note
+    {
+      if(notePtr->mode == NoteModeEmpty)  // Wenn die erste Note leer ist
+      {                                   // dann in den Wartezustand
+        next(smIdle);
+        return;
+      }
+      noteSeq[j++] = 0x90 | chn;          // ansonsten startet die Notenfolge **
+    }
+    else        // weitere Noten
+    {
+      if(notePtr->mode == NoteModeEmpty)  // bei leerer Note Schleife beendet
+        break;
+    }
+
+    // Die Noten im Akkordspeicher können durch aktuelle Noten
+    // ersetzt werden.
+
+    if(newNote[i].newVal) // wenn neue Note vorliegt
+    {
+      newNote[i].newVal = false;                  // neue Note quittieren
+      notePtr->typeIdx = newNote[i].typeIdx;      // und Inhalte im
+      notePtr->value = newNote[i].value;          // Akkordspeicher
+      notePtr->veloc = newNote[i].veloc;          // überschreiben
+    }
+
+    noteSeq[j++] = notePtr->value;        // Notenwert in Sequenz eintragen   **
+
+    // Daten für die Note aus der Typenliste holen
+    //
+    tIdx = notePtr->typeIdx;
+    typePtr = &typeList[tIdx];
+    notePtr->cntAttack  = typePtr->attack;    // Aufklingzeit in Zähler
+    notePtr->cntDecay   = typePtr->decay;     // Abklingzeit in Zähler
+    notePtr->cntSustain = typePtr->sustain;   // Klingzeit in Zähler
+    notePtr->cntRelease = typePtr->release;   // Ausklingzeit in Zähler
+    notePtr->cntPause   = typePtr->pause;     // Pausenzeit in Zähler
+
+    if(notePtr->cntAttack != 0)   // Wenn ein Attack-Wert gegeben ist
+    {
+      doAttack = true;            // dann attack markieren
+      attack =                    // und den Wert auf den erste Schritt setzen
+          (typePtr->deltaAttack * notePtr->veloc) / 100;
+      if(attack > 127) attack = 127;
+      noteSeq[j++] = attack;              // Lautstärke in Sequenz eintragen  **
+    }
+    else  // ohne Attack-Wert geht es hier gleich in Sustain weiter
+    {
+      sustain = (typePtr->percentSustain * notePtr->veloc) / 100;
+      if(sustain > 127) sustain = 127;
+      noteSeq[j++] = sustain;             // Lautstärke in Sequenz eintragen  **
+    }
+  }
+
+  crb->putSeq(noteSeq, j);                // Sequenz an Puffer übergeben   *****
+
+  if(doAttack)
+    next(smAttack);
+  else
+    next(smSustain);
+}
+
+void MidiNotes::smAttack()
+{
+
+}
+
+void MidiNotes::smDecay()
+{
+
+}
+
+// TODO
+// Es können noch nicht Noten unterschiedlicher Länge in einem Akkord
+// verarbeitet werden. Bei mehreren eingetragenen Noten würde die
+// kürzeste Note den Ablauf bestimmen.
+
+void MidiNotes::smSustain()
+{
+  int   i;
+  bool  sustFin;
+
+  sustFin = false;
+  for(i = 0; i < MaxNrNoteSim; i++)
+  {
+    notePtr = &chord[i];
+    if(notePtr->mode == NoteModeEmpty)
+      break;
+
+    if(notePtr->cntSustain > 0)     // Die Sustain-Zeit in diesem Zustand verweilen
+      notePtr->cntSustain--;
+    else
+      sustFin = true;
+  }
+
+  if(sustFin)
+    next(smNoteOff);
+}
+
+void MidiNotes::smRelease()
+{
+
+}
+
+void MidiNotes::smNoteOff()
+{
+  int   i,j;
+
+  j = 0;
+  for(i = 0; i < MaxNrNoteSim; i++)   // Alle Noten im Akkord bearbeiten
+  {
+    notePtr = &chord[i];
+    if(notePtr->mode == NoteModeEmpty)
+      break;
+
+    if(i == 0)
+    {
+      noteSeq[j++] = 0x80 | chn;      // Erste Note bestimmt den Befehl AUS    **
+      absPause = notePtr->cntPause;
+    }
+
+    noteSeq[j++] = notePtr->value;    //Erste und weitere Noten liefern Liste  **
+    noteSeq[j++] = 0;
+  }
+
+  crb->putSeq(noteSeq, j);            // Sequenz an Puffer übergeben   *****
+
+  next(smPause);
+}
+
+void MidiNotes::smPause()
+{
+  if(absPause > 0)
+  {
+    absPause--;
+    return;
+  }
+
+  next(smNoteOn);
+}
+
+// ----------------------------------------------------------------------------
+// Debugging
+// ----------------------------------------------------------------------------
+//
+
+
+
+
+
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/MidiNotes/MidiNotes.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/MidiNotes/MidiNotes.h
new file mode 100644
index 0000000000000000000000000000000000000000..0f09fccd7d1a19c3c9171b0cf96c8320f5ad0b44
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/MidiNotes/MidiNotes.h
@@ -0,0 +1,226 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   MidiNotes.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   27. November 2021
+//
+// Der Inhalt dieser Datei sind Festlegungen für steuerbare Midi-Controller
+//
+
+#ifndef MidiNotes_h
+#define MidiNotes_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+#include "ComRingBuf.h"
+
+#define MaxNrNoteSim    4
+#define MaxMidiSeq      (2 * MaxNrNoteSim + 1)
+
+// Definierte Noten
+//
+#define SchlossC    60
+#define Kammerton   69
+
+typedef enum  _NoteDiv
+{
+  nd4   = 1,
+  nd8   = 2,
+  nd16  = 4,
+  nd32  = 8,
+  nd64  = 16
+} NoteDiv;
+
+typedef enum  _MidiOpMode
+{
+  momIdle,
+  momSequence,
+  momRunDelta
+} MidiOpMode;
+
+
+// ----------------------------------------------------------------------------
+//                            M i d i N o t e s
+// ----------------------------------------------------------------------------
+//
+class MidiNotes
+{
+#define next(x) nextState = &MidiNotes::x
+
+public:
+  // -------------------------------------------------------------------------
+  // Öffentliche Datentypen
+  // -------------------------------------------------------------------------
+  //
+  typedef enum _NoteTypeIdx
+  {
+    nti0  = 0,
+    nti1,
+    nti2p,
+    nti2,
+    nti4p,
+    nti4,
+    nti8p,
+    nti8,
+    nti16p,
+    nti16,
+    nti32p,
+    nti32,
+    nti64p,
+    nti64,
+    ntiNr
+  } NoteTypeIdx;
+
+
+private:
+  // -------------------------------------------------------------------------
+  // Private Datentypen
+  // -------------------------------------------------------------------------
+  //
+  typedef void (MidiNotes::*cbVector)(void);
+
+  typedef struct _NoteType
+  {
+    dword   length;
+    dword   attack;
+    dword   decay;
+    dword   sustain;
+    dword   release;
+    dword   pause;
+    byte    deltaAttack;
+    byte    deltaDecay;
+    byte    percentSustain;
+    byte    deltaRelease;
+  } NoteType, *NoteTypePtr;
+
+  typedef struct  _Note
+  {
+    byte      mode;
+    byte      typeIdx;
+    byte      value;
+    byte      veloc;
+    int       state;
+    dword     cntAttack;
+    dword     cntDecay;
+    dword     cntSustain;
+    dword     cntRelease;
+    dword     cntPause;
+  } Note, *NotePtr;
+
+  typedef struct _NewNote
+  {
+    bool      newVal;
+    byte      typeIdx;
+    byte      value;
+    byte      veloc;
+  }NewNote;
+
+#define NoteModeEmpty     0x00
+#define NoteModeRun       0x01
+#define NoteModeDoChange  0x02
+
+  // --------------------------------------------------------------------------
+  // Lokale Daten
+  // --------------------------------------------------------------------------
+  //
+  IntrfBuf    *crb;
+  cbVector    nextState;
+
+  MidiOpMode  opMode;
+
+  dword     runCounter;
+  dword     cycleCnt;
+
+  dword     midiCycle;      // Zustandstakt in Mikrosekunden
+  dword     minNoteTick;    // minimale Notendauer in Millisekunden
+  dword     bpm;            // Beats per Minute (Metronom)
+  dword     stdNoteTick;    // Dauer einer Viertelnote in Millisekunden
+  dword     stdNoteCount;   // Viertelnote in Zyklen der Zustandsmaschine
+
+  Note      chord[MaxNrNoteSim];  // Liste der simultanen Noten (Akkord)
+  NoteType  typeList[ntiNr];      // Liste der Notentypen
+  byte      chn;                  // Aktueller Kanal
+  byte      noteSeq[MaxMidiSeq];  // Lokaler Telegrammaufbau
+
+  NotePtr     notePtr;      // Temporäre Notendaten
+  NoteTypePtr typePtr;      // Temporärer Notentyp
+
+  dword     absPause;       // Pause für den zyklischen Ablauf
+  NewNote   newNote[MaxNrNoteSim];  // Übergabe neuer Noten
+
+  bool      stopRun;           // Anhalten der Midi-Schleife
+  bool      stoppedRun;        // Midi-Schleife angehalten
+
+
+  // --------------------------------------------------------------------------
+  // Lokale Funktionen
+  // --------------------------------------------------------------------------
+  //
+
+  // Zustandsmaschine
+  // -----------------------------
+  void smInit();
+  void smIdle();
+
+  void smNoteOn();
+  void smAttack();
+  void smDecay();
+  void smSustain();
+  void smRelease();
+  void smNoteOff();
+  void smPause();
+
+
+  // --------------------------------------------------------------------------
+  // Inline-Funktionen
+  // --------------------------------------------------------------------------
+  //
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen
+  // --------------------------------------------------------------------------
+  void begin(int inBpm, NoteDiv inRes, int inMidiCycle, IntrfBuf *inCRB);
+
+
+  // --------------------------------------------------------------------------
+  // Konfiguration
+  // --------------------------------------------------------------------------
+  //
+  void  setNoteType(NoteTypeIdx nt);
+
+  void  setNoteType(NoteTypeIdx nt, byte pAttL, byte pDecL, byte pSusL, byte pRelL,
+                    byte dAtt, byte dDec, byte pSusV, byte dRel, byte pPauL);
+
+  int   addChordNote(NoteTypeIdx nti, byte val, byte vel);
+
+  void  setChannel(int chnVal);
+
+  // --------------------------------------------------------------------------
+  // Betrieb
+  // --------------------------------------------------------------------------
+  //
+  void setOpMode(MidiOpMode mom);
+  void setChordNote(int idx, NoteTypeIdx nti, int val, int vel);
+
+  // --------------------------------------------------------------------------
+  // Steuerung, Zustandsmaschine
+  // --------------------------------------------------------------------------
+  //
+  void run();
+  void stop();
+  void resume();
+
+  // --------------------------------------------------------------------------
+  // Debugging
+  // --------------------------------------------------------------------------
+  //
+
+};
+
+
+// ----------------------------------------------------------------------------
+#endif // MidiNotes_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/MidiNotes/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/MidiNotes/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..a95d68926746af5b6e20d30d2a29ea5b513110ad
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/MidiNotes/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "MidiNotes",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/Monitor/Monitor.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/Monitor/Monitor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9c30d24b20011e8d22623c261a295ccfb42fe1ea
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/Monitor/Monitor.cpp
@@ -0,0 +1,1165 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   Monitor.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   15. Mai 2021
+//
+// Der Monitor dient zum direkten Zugriff auf die Ressourcen eines
+// Mikrocontrollers über die serielle Schnittstelle.
+// ACHTUNG!
+// Er ist nicht für die Anwendung des "Serial Monitor" aus der Arduino-IDE
+// bzw. aus Eclipse/Sloeber gedacht, sondern für ein typisches "Terminal".
+// Verwendet wurde bei der Entwicklung unter Linux das GtkTerm.
+//
+
+#include "Monitor.h"
+
+//-----------------------------------------------------------------------------
+// Initialisierungen
+//-----------------------------------------------------------------------------
+
+void Monitor::init(int inMode, int inCpu, LoopCheck *inLcPtr, IntrfTw *inTwPtr)
+{
+  mode          = inMode;
+  cpu           = inCpu;
+  wrIdx         = 0;
+  rdIdx         = 0;
+  blkOut        = false;
+  blkIn         = false;
+  inIdx         = 0;
+  info          = NULL;
+  readOffsAddr  = 0;
+  doReadReg     = false;
+  extraIn       = false;
+  lcPtr         = inLcPtr;
+  twiPtr        = inTwPtr;
+  nrOfChnChar   = '@';
+
+#ifdef smnNANOBLE33
+
+  microTicValPtr  = (dword *) 0x40009548;
+  microTicCapPtr  = (dword *) 0x40009048;
+
+#endif
+
+  nextState =
+      &Monitor::waitEnter;
+}
+
+
+Monitor::Monitor(int inMode, int inCpu)
+{
+  init(inMode, inCpu, NULL, NULL);
+}
+
+Monitor::Monitor(int inMode, int inCpu, LoopCheck *inLcPtr)
+{
+  init(inMode, inCpu, inLcPtr, NULL);
+}
+
+Monitor::Monitor(int inMode, int inCpu, LoopCheck *inLcPtr, IntrfTw *inTwiPtr)
+{
+  init(inMode, inCpu, inLcPtr, inTwiPtr);
+}
+
+//-----------------------------------------------------------------------------
+// Konfiguration, Hilfsfunktionen
+//-----------------------------------------------------------------------------
+//
+int Monitor::putBuf(char c)
+{
+  if(blkIn) return(-1);
+
+  int free = rdIdx - wrIdx - 1;
+  if(free < 0) free += BufSize;
+  if(free < 1) return(0);
+  buffer[wrIdx++] = c;
+  if(wrIdx == BufSize)
+    wrIdx = 0;
+  return(1);
+}
+
+int Monitor::putBuf(char *txt)
+{
+  if(blkIn) return(-1);
+
+  int free = rdIdx - wrIdx - 1;
+  int size = strlen(txt);
+  if(free < 0) free += BufSize;
+  if(free < size) return(0);
+  for(int i = 0; i < size; i++)
+  {
+  buffer[wrIdx++] = txt[i];
+  if(wrIdx == BufSize)
+    wrIdx = 0;
+  }
+  return(size);
+}
+
+char Monitor::getBuf()
+{
+  int num = wrIdx - rdIdx;
+  if(num == 0) return('\0');
+  char c = buffer[rdIdx++];
+  if(rdIdx == BufSize)
+    rdIdx = 0;
+  return(c);
+}
+
+void Monitor::clrBuf()
+{
+  wrIdx = 0;
+  rdIdx = 0;
+}
+
+void Monitor::sendConfig()
+{
+  int           nrChn   = nrOfChnChar & 0x3F;
+  int           bufIdx  = 0;
+  CfgMeasChnPtr cfgPtr;
+
+  // Visualisierungskanäle (Anzahl) anfordern
+  outChar[bufIdx++] = '&';
+  outChar[bufIdx++] = '@';    // Kanalnummer
+  outChar[bufIdx++] = '@';    // Typ
+  outChar[bufIdx++] = nrOfChnChar;
+  outChar[bufIdx++] = '$';
+
+  for(int i = 0; i < nrChn; i++)
+  {
+    cfgPtr = &cfgChnArr[i];
+    outChar[bufIdx++] = '&';
+    outChar[bufIdx++] = (i+1) | 0x40;
+    outChar[bufIdx++] = cfgPtr->type;
+    hexWord(&outChar[bufIdx], cfgPtr->maxVal);
+    bufIdx += 4;
+    hexWord(&outChar[bufIdx], cfgPtr->minVal);
+    bufIdx += 4;
+    if(cfgPtr->name != NULL)
+    {
+      bufIdx += cpyStr(&outChar[bufIdx], cfgPtr->name);
+    }
+    outChar[bufIdx++] = '$';
+  }
+  outChar[bufIdx] = '\0';
+  out(outChar);
+}
+
+//-----------------------------------------------------------------------------
+// Lokale Abläufe
+//-----------------------------------------------------------------------------
+//
+
+volatile dword calcTest1, calcTest2, calcTest3;
+
+void Monitor::waitEnter()
+{
+  char  c;
+
+  busy = false;
+
+  if(!keyHit()) return;
+  c = keyIn();
+  if(c != '\r' && c != '\n')
+  {
+    lastKeyIn = c;
+    return;
+  }
+
+  busy = true;
+
+  blkIn   = true;
+  blkOut  = true;
+  GoPrm
+}
+
+void doSomeCode()
+{
+  for(int i = 0; i < 500; i++)
+  {
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+  }
+}
+
+volatile dword micTime;
+
+void Monitor::getKey()
+{
+  char  cin,c1,c0;
+
+  if(!keyHit()) return;
+  cin = keyIn();
+  if(mode & modeEcho)
+    out(cin);
+
+  c0 = c1 = '\0';
+
+  if(inIdx == 0)
+    c0 = cin;
+  else if(inIdx == 1)
+  {
+    c0 = inChar[0];
+    c1 = cin;
+  }
+  else if(inIdx == 2)
+  {
+    c0 = inChar[0];
+    c1 = inChar[1];
+  }
+
+  switch(c0)
+  {
+    case '\r':
+      out("\r\n");
+      blkIn   = false;
+      blkOut  = false;
+      GoWt
+      break;
+
+    case 'c':
+    case 'C':
+      if(inIdx == 0)
+      {
+        inChar[inIdx] = cin;
+        inIdx++;
+      }
+      else if(inIdx == 1)
+      {
+
+        if(cin >= '0' && cin <= '9')
+        {
+          inIdx = 0;
+          out('=');
+          int cIdx = cin - 0x30;
+          if(cFlag[cIdx])
+          {
+            cFlag[cIdx] = false;
+            out('0');
+          }
+          else
+          {
+            cFlag[cIdx] = true;
+            out('1');
+          }
+          GoPrm
+        }
+
+        else if(cin == 'f' || cin == 'F')
+        {
+          inChar[inIdx] = cin;
+          inIdx++;
+        }
+      }
+
+      else if(inIdx == 2)
+      {
+        inIdx = 0;
+        if(cin == 'g' || cin == 'G')
+        {
+          sendConfig();
+          GoPrm
+        }
+      }
+      else
+        GoPrm
+      break;
+
+    case 'i':
+    case 'I':
+      if(inIdx == 0)
+      {
+        inChar[inIdx] = cin;
+        inIdx++;
+      }
+      else if(inIdx == 1)
+      {
+        inIdx = 0;
+        out(' ');
+        if(cin == 'a' || cin == 'A')
+          nextState = &Monitor::getTwiAdr;
+        else if(cin == 'l' || cin == 'L')
+          nextState = &Monitor::readTwiList;
+        else if(cin == 'r' || cin == 'R')
+          nextState = &Monitor::readTwiByte;
+        else if(cin == 'w' || cin == 'W')
+          nextState = &Monitor::writeTwiByte;
+      }
+      break;
+
+    case 'r':
+    case 'R':
+      if(inIdx == 0)
+      {
+        inChar[inIdx] = cin;
+        inIdx++;
+      }
+      else if(inIdx == 1)
+      {
+        inIdx = 0;
+        out(' ');
+        if(cin == 'o' || cin == 'O')
+          nextState = &Monitor::getRdOffsAdr;
+        else if(cin == 'r' || cin == 'R')
+          nextState = &Monitor::readRegVal;
+      }
+      break;
+
+    // ------------------------------------------------------------------------
+    case 't':     //                 Zeitmessungen
+    case 'T':     //
+    // ------------------------------------------------------------------------
+      if(inIdx == 0)
+      {
+        inChar[inIdx] = cin;
+        inIdx++;
+      }
+      else if(inIdx == 1)
+      {
+        inIdx = 0;
+        nextState = &Monitor::getTiming;
+        if(cin == 'l' || cin == 'L')
+        {
+          cmdMode1 = 'L';
+        }
+        else if(cin == 'b' || cin == 'B')
+        {
+          cmdMode1 = 'B';
+        }
+        else if(cin == 'c' || cin == 'C')
+        {
+          cmdMode1 = 'C';
+        }
+#ifdef smnNANOBLE33
+        else if(cin == 'p' || cin == 'P')
+        {
+          micTime = micsecs();
+          doSomeCode();
+          micTime = (micsecs() - micTime - 11) / 10;
+          out(' ');
+          out(micTime);
+          GoPrm
+        }
+#endif
+        else if(cin == 'r' || cin == 'R')
+        {
+          if(lcPtr != NULL)
+          {
+            lcPtr->resetStatistics();
+            out(' ');
+          }
+          GoPrm
+        }
+        else if(cin == 't' || cin == 'T')
+        {
+          dword micTime = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          micTime = (micros() - micTime) / 20;
+          out(' ');
+          out(micTime);
+          GoPrm
+        }
+        else
+        {
+          GoPrm
+        }
+      }
+      break;
+
+    case 'V':
+    case 'v':
+      out(' ');
+      nextState = &Monitor::version;
+      break;
+
+  }
+}
+
+void Monitor::prompt()
+{
+  if(mode & modeNl)
+    out('\n');
+
+  out("\rM>");
+  GoInp
+}
+
+void Monitor::version()
+{
+  out("Monitor: Version 0.1, May 16, 2021");
+  GoPrm
+}
+
+void Monitor::getRdOffsAdr()
+{
+  char  cin;
+  dword val;
+  int   valIdx;
+
+  if(!keyHit()) return;
+  cin = keyIn();
+  readOffsAddr = 0;
+
+  if(cin != '\r')
+  {
+    if(cin >= '0' && cin <= '9')
+      inChar[inIdx] = cin - 0x30;
+    else if(cin >= 'A' && cin <= 'F')
+      inChar[inIdx] = cin - 0x37;
+    else if(cin >= 'a' && cin <= 'f')
+      inChar[inIdx] = cin - 0x57;
+    else
+      return;
+
+    out(cin);
+    if(inIdx < 16)
+      inIdx++;
+    else
+      out((char) 0x08);
+  }
+  else
+  {
+    valIdx = 0;
+    inIdx--;
+    while(inIdx >= 0)
+    {
+      val = inChar[inIdx];
+      readOffsAddr |= val << valIdx * 4;
+      inIdx--;
+      valIdx++;
+    }
+
+    // TEST
+    //out(" = ");
+    //out(readOffsAddr);
+    inIdx = 0;
+    GoPrm
+  }
+}
+
+void Monitor::readRegVal()
+{
+  char  cin;
+  dword val,adr;
+  int   valIdx;
+
+  dword *regPtr;
+
+  if(!keyHit()) return;
+  cin = keyIn();
+  adr = 0;
+
+  if(cin != '\r')
+  {
+    if(cin >= '0' && cin <= '9')
+      inChar[inIdx] = cin - 0x30;
+    else if(cin >= 'A' && cin <= 'F')
+      inChar[inIdx] = cin - 0x37;
+    else if(cin >= 'a' && cin <= 'f')
+      inChar[inIdx] = cin - 0x57;
+    else
+      return;
+
+    out(cin);
+    if(inIdx < 16)
+      inIdx++;
+    else
+      out((char) 0x08);
+  }
+  else
+  {
+    valIdx = 0;
+    inIdx--;
+    while(inIdx >= 0)
+    {
+      val = inChar[inIdx];
+      adr |= val << valIdx * 4;
+      inIdx--;
+      valIdx++;
+    }
+
+    regPtr = (dword *) (readOffsAddr + adr);
+    val = *regPtr;
+
+    out(": ");
+    hexDword(outChar, val);
+    out(outChar);
+    inIdx = 0;
+    GoPrm
+  }
+}
+
+void Monitor::getTiming()
+{
+  LoopStatistics  lStat;
+  unsigned int    maxVal;
+  unsigned int    minVal;
+  unsigned int    avgVal;
+  char  cin;
+
+  if(lcPtr == NULL)
+  {
+    outl("Kein LoopCheck.");
+    GoPrm
+    return;
+  }
+
+  if(!keyHit()) return;
+  cin = keyIn();
+  if(mode & modeEcho)
+    out(cin);
+
+  if(cin == 'r' || cin == 'R')
+  {
+    out(' ');
+    lcPtr->getStatistics(&lStat);
+    if(cmdMode1 == 'L')
+    {
+      maxVal = lStat.loopMaxTime;
+      minVal = lStat.loopMinTime;
+      avgVal = lStat.loopAvgTime;
+    }
+    else if(cmdMode1 == 'B')
+    {
+      maxVal = lStat.bgMaxTime;
+      minVal = lStat.bgMinTime;
+      avgVal = lStat.bgAvgTime;
+    }
+    else if(cmdMode1 == 'C')
+    {
+      maxVal = lStat.maxPeriod;
+      minVal = lStat.minPeriod;
+      avgVal = lStat.avgPeriod;
+    }
+    else
+    {
+      maxVal = 0;
+      minVal = 0;
+      avgVal = 0;
+    }
+
+    out(maxVal); out("/"); out(minVal); out("/"); out(avgVal); out("\r");
+    GoPrm
+  }
+  else if(cin == 'm' || cin == 'M')
+  {
+    lcPtr->resetStatistics();
+    lcPtr->startTimeMeasure();
+    nextState = &Monitor::getLoopMeasure;
+  }
+
+}
+
+void Monitor::getLoopMeasure()
+{
+  LoopStatistics  lStat;
+  unsigned int    maxVal;
+  unsigned int    minVal;
+  unsigned int    avgVal;
+
+  if(lcPtr->getTimeMeasure() < 1000000)
+    return;
+
+  out(' ');
+  lcPtr->getStatistics(&lStat);
+  if(cmdMode1 == 'L')
+  {
+    maxVal = lStat.loopMaxTime;
+    minVal = lStat.loopMinTime;
+    avgVal = lStat.loopAvgTime;
+  }
+  else if(cmdMode1 == 'B')
+  {
+    maxVal = lStat.bgMaxTime;
+    minVal = lStat.bgMinTime;
+    avgVal = lStat.bgAvgTime;
+  }
+  else if(cmdMode1 == 'C')
+  {
+    maxVal = lStat.maxPeriod;
+    minVal = lStat.minPeriod;
+    avgVal = lStat.avgPeriod;
+  }
+  else
+  {
+    maxVal = 0;
+    minVal = 0;
+    avgVal = 0;
+  }
+
+  out(maxVal); out("/"); out(minVal); out("/"); out(avgVal); out("\r");
+  GoPrm
+}
+
+
+void Monitor::getTwiAdr()
+{
+  char  cin;
+
+  if(!keyHit()) return;
+  cin = keyIn();
+  twiAdr = 0;
+
+  if(cin != '\r')
+  {
+    if(cin >= '0' && cin <= '9')
+      inChar[inIdx] = cin - 0x30;
+    else if(cin >= 'A' && cin <= 'F')
+      inChar[inIdx] = cin - 0x37;
+    else if(cin >= 'a' && cin <= 'f')
+      inChar[inIdx] = cin - 0x57;
+    else
+      return;
+
+    out(cin);
+    if(inIdx < 2)
+      inIdx++;
+    else
+      out((char) 0x08);
+  }
+  else
+  {
+    twiAdr = (inChar[0] << 4) | inChar[1];
+
+    out(" = ");
+    out(twiAdr);
+
+    inIdx = 0;
+    GoPrm
+  }
+}
+
+void Monitor::readTwiList()
+{
+  char  cin;
+  int   reg;
+  int   anz;
+
+  char  tmpOut[3];
+
+  TwiStatus twiStatus;
+
+  if(twiPtr == NULL)
+  {
+    out("no Twi");
+    inIdx = 0;
+    GoPrm
+  }
+
+  if(!keyHit()) return;
+  cin = keyIn();
+
+  if(cin != '\r')
+  {
+    if(cin >= '0' && cin <= '9')
+      inChar[inIdx] = cin - 0x30;
+    else if(cin >= 'A' && cin <= 'F')
+      inChar[inIdx] = cin - 0x37;
+    else if(cin >= 'a' && cin <= 'f')
+      inChar[inIdx] = cin - 0x57;
+    else
+      return;
+
+    out(cin);
+    if(inIdx == 1)
+      out(" ");
+
+    if(inIdx < 4)
+      inIdx++;
+    else
+      out((char) 0x08);
+  }
+  else
+  {
+    reg = (inChar[0] << 4) | inChar[1];
+    anz = (inChar[2] << 4) | inChar[3];
+
+    twiByteSeq.len = anz;
+    twiByteSeq.valueRef = byteArray;
+
+    twiStatus = twiPtr->readByteRegSeq(twiAdr, reg, &twiByteSeq);
+
+    out(" [");
+    out((int) twiStatus);
+    out("] ");
+
+    if((int) twiStatus == 128)
+    {
+      for(int i = 0; i < anz; i++)
+      {
+        hexByte(tmpOut, byteArray[i]);
+        out(tmpOut);
+        if(i != (anz-1)) out(':');
+      }
+    }
+
+    inIdx = 0;
+    GoPrm
+  }
+}
+
+void Monitor::readTwiByte()
+{
+  char  cin;
+  int   reg;
+  byte  val;
+
+  if(twiPtr == NULL)
+  {
+    out("no Twi");
+    inIdx = 0;
+    GoPrm
+  }
+
+  if(!keyHit()) return;
+  cin = keyIn();
+
+  if(cin != '\r')
+  {
+    if(cin >= '0' && cin <= '9')
+      inChar[inIdx] = cin - 0x30;
+    else if(cin >= 'A' && cin <= 'F')
+      inChar[inIdx] = cin - 0x37;
+    else if(cin >= 'a' && cin <= 'f')
+      inChar[inIdx] = cin - 0x57;
+    else
+      return;
+
+    out(cin);
+    if(inIdx < 2)
+      inIdx++;
+    else
+      out((char) 0x08);
+  }
+  else
+  {
+    reg = (inChar[0] << 4) | inChar[1];
+
+    val = twiPtr->readByteReg(twiAdr, reg);
+
+    out(" = ");
+    binByte(outChar, val);
+    out(outChar);
+
+    inIdx = 0;
+    GoPrm
+  }
+}
+
+void Monitor::writeTwiByte()
+{
+  char  cin;
+  int   reg;
+  int   val;
+
+  TwiStatus twiStatus;
+
+  if(twiPtr == NULL)
+  {
+    out("no Twi");
+    inIdx = 0;
+    GoPrm
+  }
+
+  if(!keyHit()) return;
+  cin = keyIn();
+
+  if(cin != '\r')
+  {
+    if(cin >= '0' && cin <= '9')
+      inChar[inIdx] = cin - 0x30;
+    else if(cin >= 'A' && cin <= 'F')
+      inChar[inIdx] = cin - 0x37;
+    else if(cin >= 'a' && cin <= 'f')
+      inChar[inIdx] = cin - 0x57;
+    else
+      return;
+
+    out(cin);
+    if(inIdx == 1)
+      out(" ");
+
+    if(inIdx < 4)
+      inIdx++;
+    else
+      out((char) 0x08);
+  }
+  else
+  {
+    reg = (inChar[0] << 4) | inChar[1];
+    val = (inChar[2] << 4) | inChar[3];
+
+    twiStatus = twiPtr->writeByteReg(twiAdr, reg, val);
+
+    out(" : ");
+    out((int) twiStatus);
+
+    inIdx = 0;
+    GoPrm
+  }
+}
+
+//-----------------------------------------------------------------------------
+// Lokale Schnittstelle
+//-----------------------------------------------------------------------------
+//
+void Monitor::print(char c, int eol)
+{
+  putBuf(c);
+  if(eol & eolCR)
+    putBuf('\r');
+  if(eol & eolLF)
+    putBuf('\n');
+}
+
+void Monitor::print(char *txt, int eol)
+{
+  if(txt != NULL)
+    putBuf(txt);
+  if(eol & eolCR)
+    putBuf('\r');
+  if(eol & eolLF)
+    putBuf('\n');
+}
+
+void Monitor::print(byte *hex, int nr, char fill, int eol)
+{
+  if(hex == NULL) return;
+
+  for(int i = 0; i < nr; i++)
+  {
+    hexByte(tmpChar,hex[i]);
+    tmpChar[2] = fill;
+    tmpChar[3] = '\0';
+    putBuf(tmpChar);
+  }
+
+  if(eol & eolCR)
+    putBuf('\r');
+  if(eol & eolLF)
+    putBuf('\n');
+}
+
+void Monitor::print(unsigned int iVal, int eol)
+{
+  char  iBuf[16];
+
+  itoa(iVal, iBuf, 10);
+  putBuf(iBuf);
+  if(eol & eolCR)
+    putBuf('\r');
+  if(eol & eolLF)
+    putBuf('\n');
+}
+
+void Monitor::prints(int iVal, int eol)
+{
+  char  iBuf[16];
+
+  itoa(iVal, iBuf, 10);
+  putBuf(iBuf);
+  if(eol & eolCR)
+    putBuf('\r');
+  if(eol & eolLF)
+    putBuf('\n');
+}
+
+//-----------------------------------------------------------------------------
+// Datenaufbereitung
+//-----------------------------------------------------------------------------
+//
+void Monitor::hexByte(char * dest, byte val)
+{
+  char cv;
+
+  cv = val >> 4;
+  if(cv < 10)
+    cv += 0x30;
+  else
+    cv += 0x37;
+  dest[0] = cv;
+
+  cv = val & 0x0F;
+  if(cv < 10)
+    cv += 0x30;
+  else
+    cv += 0x37;
+  dest[1] = cv;
+
+  dest[2] = '\0';
+}
+
+void Monitor::binByte(char * dest, byte val)
+{
+  byte mask;
+
+  mask = 0x80;
+
+  for(int i = 0; i < 8; i++)
+  {
+    if((val & mask) != 0)
+      dest[i] = '1';
+    else
+      dest[i] = '0';
+    mask >>= 1;
+  }
+
+  dest[8] = '\0';
+}
+
+int   Monitor::cpyStr(char *dest, char *src)
+{
+  int   i = 0;
+
+  while((dest[i] = src[i]) != '\0') i++;
+  return(i);
+}
+
+void Monitor::binDword(char *dest, dword dwVal)
+{
+  int   idx = 0;
+  byte  bVal;
+
+  bVal = dwVal >> 24;
+  binByte(&dest[idx], bVal);
+  idx += 8;
+  dest[idx++] = ' ';
+
+  bVal = dwVal >> 16;
+  binByte(&dest[idx], bVal);
+  idx += 8;
+  dest[idx++] = ' ';
+
+  bVal = dwVal >> 8;
+  binByte(&dest[idx], bVal);
+  idx += 8;
+  dest[idx++] = ' ';
+
+  bVal = dwVal;
+  binByte(&dest[idx], bVal);
+  idx += 8;
+
+  dest[idx] = '\0';
+}
+
+void Monitor::hexDword(char *dest, dword dwVal)
+{
+  int   idx = 0;
+  byte  bVal;
+
+  bVal = dwVal >> 24;
+  hexByte(&dest[idx], bVal);
+  idx += 2;
+
+  bVal = dwVal >> 16;
+  hexByte(&dest[idx], bVal);
+  idx += 2;
+
+  bVal = dwVal >> 8;
+  hexByte(&dest[idx], bVal);
+  idx += 2;
+
+  bVal = dwVal;
+  hexByte(&dest[idx], bVal);
+  idx += 2;
+
+  dest[idx] = '\0';
+}
+
+void Monitor::binWord(char *dest, word wVal)
+{
+  int   idx = 0;
+  byte  bVal;
+
+  bVal = wVal >> 8;
+  binByte(&dest[idx], bVal);
+  idx += 8;
+  dest[idx++] = ' ';
+
+  bVal = wVal;
+  binByte(&dest[idx], bVal);
+  idx += 8;
+
+  dest[idx] = '\0';
+}
+
+void Monitor::hexWord(char *dest, word wVal)
+{
+  int   idx = 0;
+  byte  bVal;
+
+  bVal = wVal >> 8;
+  hexByte(&dest[idx], bVal);
+  idx += 2;
+
+  bVal = wVal;
+  hexByte(&dest[idx], bVal);
+  idx += 2;
+
+  dest[idx] = '\0';
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Anwenderschnittstelle
+//-----------------------------------------------------------------------------
+//
+
+void Monitor::run()
+{
+  char c;
+
+  if(!blkOut)
+  {
+    c = getBuf();
+    if(c != '\0')
+      smnSerial.print(c);
+  }
+  (this->*nextState)();
+}
+
+void Monitor::cprint(char c)
+{
+  print(c, 0);
+}
+
+void Monitor::cprintln(char c)
+{
+  print(c, eolCR | eolLF);
+}
+
+void Monitor::cprintcr(char c)
+{
+  print(c, eolCR);
+}
+
+void Monitor::print(char *txt)
+{
+  print(txt, 0);
+}
+
+void Monitor::println(char *txt)
+{
+  print(txt, eolCR | eolLF);
+}
+
+void Monitor::println()
+{
+  print((char *) NULL, eolCR | eolLF);
+}
+
+void Monitor::printcr(char *txt)
+{
+  print(txt, eolCR);
+}
+
+void Monitor::printcr()
+{
+  print((char *) NULL, eolCR);
+}
+
+void Monitor::print(unsigned int iVal)
+{
+  print(iVal, 0);
+}
+
+void Monitor::prints(int iVal)
+{
+  prints(iVal, 0);
+}
+
+void Monitor::println(unsigned int iVal)
+{
+  print(iVal, eolCR | eolLF);
+}
+
+void Monitor::printcr(unsigned int iVal)
+{
+  print(iVal, eolCR);
+}
+
+void Monitor::print(byte *iVal, int nr, char fill)
+{
+  print(iVal, nr, fill, 0);
+}
+
+void Monitor::printcr(byte *iVal, int nr, char fill)
+{
+  print(iVal, nr, fill, eolCR);
+}
+
+void Monitor::println(byte *iVal, int nr, char fill)
+{
+  print(iVal, nr, fill, eolCR | eolLF);
+}
+
+void Monitor::setInfo(char *txt)
+{
+  info = txt;
+}
+
+void Monitor::config(int inNrOfChn)
+{
+  if(inNrOfChn < 0) return;
+
+  if(inNrOfChn > MaxChn)
+    inNrOfChn = MaxChn;
+
+  nrOfChnChar = inNrOfChn | 0x40;
+}
+
+void Monitor::config(int inChn, char inType, word inMax, word inMin, char *inName)
+{
+  if(inChn < 1) return;
+
+  if(inChn > MaxChn)
+    inChn = MaxChn;
+
+  CfgMeasChnPtr chnPtr = &cfgChnArr[inChn-1];
+  chnPtr->maxVal = inMax;
+  chnPtr->minVal = inMin;
+  chnPtr->name = inName;
+  chnPtr->type = inType;
+}
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/Monitor/Monitor.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/Monitor/Monitor.h
new file mode 100644
index 0000000000000000000000000000000000000000..1ee2bb11830c716703e5489655355c93a039d131
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/Monitor/Monitor.h
@@ -0,0 +1,211 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   Monitor.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   15. Mai 2021
+//
+// Der Monitor dient zum direkten Zugriff auf die Ressourcen eines
+// Mikrocontrollers über die serielle Schnittstelle.
+//
+
+#include  "Arduino.h"
+#include  "environment.h"
+#include  "arduinoDefs.h"
+#include  "LoopCheck.h"
+#include  "IntrfTw.h"
+
+#ifndef Monitor_h
+#define Monitor_h
+// ----------------------------------------------------------------------------
+
+#define keyHit()  smnSerial.available()
+#define keyIn()   smnSerial.read()
+#define out(x)    smnSerial.print(x)
+#define outl(x)   smnSerial.println(x)
+#define GoInp     nextState = &Monitor::getKey;
+#define GoPrm     nextState = &Monitor::prompt;
+#define GoWt      nextState = &Monitor::waitEnter;
+
+#define modeEcho  0x01
+#define modeNl    0x02
+
+#define eolCR     0x01
+#define eolLF     0x02
+#define eolNL     0x03
+
+#define BufSize   512
+#define MaxChn    32
+
+class Monitor
+{
+  // -------------------------------------------------------------------------
+  // class specific data types
+  // -------------------------------------------------------------------------
+  //
+  typedef void (Monitor::*StatePtr)(void);
+  typedef struct _ConfMeasChannel
+  {
+    word    maxVal;
+    word    minVal;
+    char    *name;
+    char    type;
+  } CfgMeasChn, *CfgMeasChnPtr;
+
+private:
+  // --------------------------------------------------------------------------
+  // Lokale Daten
+  // --------------------------------------------------------------------------
+  //
+  int       cpu;
+  int       mode;
+
+#ifdef smnNANOBLE33
+  dword     *microTicValPtr;
+  dword     *microTicCapPtr;
+#endif
+
+  char      buffer[BufSize];
+  int       wrIdx;
+  int       rdIdx;
+  bool      blkOut;
+  bool      blkIn;
+
+  char      inChar[16];
+  char      outChar[128];
+  char      tmpChar[8];
+  int       inIdx;
+  bool      extraIn;
+
+  char      cmdMode1;
+  char      cmdMode2;
+
+  char      *info;
+
+  StatePtr  nextState;
+  LoopCheck *lcPtr;
+
+  IntrfTw     *twiPtr;
+  int         twiAdr;
+  TwiByteSeq  twiByteSeq;
+  byte        byteArray[32];
+
+  dword       readOffsAddr;
+  bool        doReadReg;
+
+  CfgMeasChn  cfgChnArr[MaxChn];
+  char        nrOfChnChar;
+
+  // --------------------------------------------------------------------------
+  // Lokale Funktionen
+  // --------------------------------------------------------------------------
+  //
+  void  init(int mode, int cpu);
+  void  init(int mode, int cpu, LoopCheck *inLcPtr);
+  void  init(int mode, int cpu, LoopCheck *inLcPtr, IntrfTw *inTwPtr);
+
+  void  waitEnter();
+  void  prompt();
+  void  getKey();
+  void  version();
+  void  getRdOffsAdr();
+  void  readRegVal();
+  void  getTiming();
+  void  getLoopMeasure();
+
+  void  getTwiAdr();
+  void  readTwiList();
+  void  readTwiByte();
+  void  writeTwiByte();
+
+  void  print(char c, int eol);
+  void  print(char *txt, int eol);
+  void  print(byte *hex, int nr, char fill, int eol);
+  void  print(unsigned int iVal, int eol);
+  void  prints(int iVal, int eol);
+
+#ifdef smnNANOBLE33
+
+  dword micsecs()
+  {
+    *microTicCapPtr = 1;
+    return(*microTicValPtr);
+  }
+
+#endif
+
+  // --------------------------------------------------------------------------
+  // Datenaufbereitung
+  // --------------------------------------------------------------------------
+  //
+  void hexByte(char *dest, byte val);
+  void binByte(char *dest, byte val);
+  void hexWord(char *dest, word val);
+  void binWord(char *dest, word val);
+  void hexDword(char *dest, dword val);
+  void binDword(char *dest, dword val);
+  int  cpyStr(char *dest, char *src);
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen
+  // --------------------------------------------------------------------------
+
+  Monitor(int mode, int cpu);
+  Monitor(int mode, int cpu, LoopCheck *inLcPtr);
+  Monitor(int mode, int cpu, LoopCheck *inLcPtr, IntrfTw *inTwiPtr);
+
+  // --------------------------------------------------------------------------
+  // Konfiguration und Hilfsfunktionen
+  // --------------------------------------------------------------------------
+  //
+  void  setInfo(char *txt);
+  int   putBuf(char c);
+  int   putBuf(char *txt);
+  char  getBuf();
+  void  clrBuf();
+  void  sendConfig();
+
+
+  // --------------------------------------------------------------------------
+  // Anwenderschnittstelle
+  // --------------------------------------------------------------------------
+  //
+
+  // Funktionen
+  //
+  void run();
+  void cprint(char c);
+  void print(char *txt);
+  void print(unsigned int iVal);
+  void prints(int iVal);
+  void print(byte *iVal, int nr, char fill);
+  void printcr();
+  void cprintcr(char c);
+  void printcr(char *txt);
+  void printcr(unsigned int iVal);
+  void printcr(byte *iVal, int nr, char fill);
+  void println();
+  void cprintln(char c);
+  void println(char *txt);
+  void println(unsigned int iVal);
+  void println(byte *iVal, int nr, char fill);
+
+  void config(int inNrOfChn);
+  void config(int inChn, char inType, word inMax, word inMin, char *inName);
+
+
+  // Zustände (Variablen)
+  //
+  bool  busy;
+  char  lastKeyIn;
+
+  // Steuerbits (Kommandobits)
+  //
+  bool  cFlag[10];
+  };
+
+// ----------------------------------------------------------------------------
+#endif // Monitor_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/Monitor/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/Monitor/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..a9b281138760d16aa71d51ecec1518e22188a30f
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/Monitor/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "Monitor",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SensorLSM9DS1/SensorLSM9DS1.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SensorLSM9DS1/SensorLSM9DS1.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5a57198a5bb3b3c340ffa5a725f7f7c009725d9a
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SensorLSM9DS1/SensorLSM9DS1.cpp
@@ -0,0 +1,875 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   SensorLSM9DS1.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#include "SensorLSM9DS1.h"
+#include <string.h>
+
+// ----------------------------------------------------------------------------
+// Initialisierungen
+// ----------------------------------------------------------------------------
+
+SensorLSM9DS1::SensorLSM9DS1(IntrfTw *refI2C, int inRunCycle)
+{
+  TwiParams twiParams;
+
+  twPtr               = refI2C;
+  runState            = rsInit;
+  twiByteSeq.len      = 12;
+  twiByteSeq.valueRef = byteArray;
+  runCycle            = inRunCycle;
+
+  fullScaleA  = 4;
+  fullScaleG  = 2000;
+  fullScaleM  = 4;
+
+  newValueAG  = false;
+  newValueM   = false;
+
+  avgSetAG    = 0;
+  avgCntAG    = 0;
+  avgSetM     = 0;
+  avgCntM     = 0;
+
+  errorCntAdrNakAG    = 0;
+  errorCntDataNakAG   = 0;
+  errorCntOverAG      = 0;
+
+  errorCntAdrNakM     = 0;
+  errorCntDataNakM    = 0;
+  errorCntOverM       = 0;
+
+  timeOutTwiStatus    = 0;
+  timeOutTwiDataAG    = 0;
+  timeOutTwiDataM     = 0;
+
+  timeOutStatusAG     = 0;
+  toValueStatusAG     = 0;
+  timeOutStatusM      = 0;
+  toValueStatusM      = 0;
+
+  toCntTwiStatusAG  = 0;
+  toCntTwiStatusM   = 0;
+  toCntTwiDataAG    = 0;
+  toCntTwiDataM     = 0;
+  toCntStatusAG     = 0;
+  toCntStatusM      = 0;
+
+  sumA.x = sumA.y = sumA.z = sumG.x = sumG.y = sumG.z = 0;
+  waitCnt = 2;
+
+  refI2C->getParams(&twiParams);
+  switch(twiParams.speed)
+  {
+    case Twi100k:
+      twiCycle = 10;
+      break;
+
+    case Twi250k:
+      twiCycle = 4;
+      break;
+
+    case Twi400k:
+      twiCycle = 2;
+      break;
+  }
+
+  twiStatusCycle  = 40 * twiCycle;
+  twiDataCycleAG  = 160 * twiCycle;
+  twiDataCycleM   = 100 * twiCycle;
+
+  enableMeasAG  = false;
+  enableMeasM   = false;
+
+  runStateCntTotal = 0;
+}
+
+// ----------------------------------------------------------------------------
+// Konfiguration
+// ----------------------------------------------------------------------------
+//
+int SensorLSM9DS1::resetAG()
+{
+  twPtr->writeByteReg(AG_Adr, AG_Ctrl8, 0x05);
+
+  // Get ID
+  return(twPtr->readByteReg(AG_Adr, AG_Id));
+}
+
+int SensorLSM9DS1::resetM()
+{
+  twPtr->writeByteReg(M_Adr, M_Ctrl2, 0x0C);
+
+  // Get ID
+  return(twPtr->readByteReg(M_Adr, M_Id));
+}
+
+int SensorLSM9DS1::reset()
+{
+  int retv;
+
+  twPtr->writeByteReg(AG_Adr, AG_Ctrl8, 0x05);
+  twPtr->writeByteReg(M_Adr, M_Ctrl2, 0x0C);
+
+  retv = twPtr->readByteReg(AG_Adr, AG_Id);
+  retv += twPtr->readByteReg(M_Adr, M_Id);
+  return(retv);
+}
+
+void SensorLSM9DS1::setScanAG(byte scValueAG, byte scValueA, byte scValueG)
+{
+  twPtr->writeByteReg(AG_Adr, AG_Ctrl6, scValueAG | scValueA);
+  twPtr->writeByteReg(AG_Adr, AG_Ctrl1, scValueAG | scValueG);
+}
+
+void SensorLSM9DS1::setScanM(byte scValue1, byte scValue2, byte scValue3, byte scValue4)
+{
+  twPtr->writeByteReg(M_Adr, M_Ctrl1, scValue1);
+  twPtr->writeByteReg(M_Adr, M_Ctrl2, scValue2);
+  twPtr->writeByteReg(M_Adr, M_Ctrl3, scValue3);
+  twPtr->writeByteReg(M_Adr, M_Ctrl4, scValue4);
+}
+
+void SensorLSM9DS1::setTimeOutValues(FreqAG fAG, FreqM fM)
+{
+  int freqA = 1190, freqM = 40000;
+  int cycleA, cycleM;
+
+  enableMeasAG = true;
+
+  switch(fAG)
+  {
+    case FreqAG14_9:
+      freqA = 149;
+      break;
+
+    case FreqAG59_5:
+      freqA = 595;
+      break;
+
+    case FreqAG119:
+      freqA = 1190;
+      break;
+
+    case FreqAG238:
+      freqA = 2380;
+      break;
+
+    case FreqAG476:
+      freqA = 4760;
+      break;
+
+    case FreqAG952:
+      freqA = 9520;
+      break;
+
+    case FreqAG_OFF:
+      freqA = 1190;
+      enableMeasAG = false;
+      break;
+  }
+
+  cycleA = (freqA * runCycle) / 10;     // Zyklusfrequenz
+  toValueStatusAG = 1200000 / cycleA;   // Mikrosekunden + 20% Verlängerung
+
+  // Test
+  //toValueStatusAG += 2;
+
+  enableMeasM = true;
+
+  switch(fM)
+  {
+    case FreqM0_625:
+      freqM = 625;
+      break;
+
+    case FreqM1_25:
+      freqM = 1250;
+      break;
+
+    case FreqM2_5:
+      freqM = 2500;
+      break;
+
+    case FreqM5:
+      freqM = 5000;
+      break;
+
+    case FreqM10:
+      freqM = 10000;
+      break;
+
+    case FreqM20:
+      freqM = 20000;
+      break;
+
+    case FreqM40:
+      freqM = 40000;
+      break;
+
+    case FreqM80:
+      freqM = 80000;
+      break;
+
+    case FreqM_OFF:
+      freqM = 40000;
+      enableMeasM = false;
+      break;
+  }
+
+  cycleM = (freqM * runCycle) / 1000;   // Zyklusfrequenz
+  toValueStatusM = 1200000 / cycleM;    // Mikrosekunden + 20% Verlängerung
+
+  // Test
+  //toValueStatusM += 3;
+
+}
+
+void SensorLSM9DS1::begin(FreqAG freqAG, int avgAG, MaxA maxA, MaxG maxG, FreqM freqM, int avgM, MaxM maxM)
+{
+  setScanAG((byte) freqAG, (byte) maxA | A_LpAuto, (byte) maxG | G_LpHH);
+  setScanM((byte) freqM | Mxy_PmMed | M_TmpOn, (byte) maxM, M_Contin, Mz_PmMed);
+
+  setTimeOutValues(freqAG, freqM);
+
+  if(avgAG == 1)
+  {
+    avgSetAG = 0;
+    avgCntAG = 0;
+  }
+  else
+  {
+    avgSetAG = avgAG;
+    avgCntAG = avgAG;
+  }
+
+  if(avgM == 1)
+  {
+    avgSetM = 0;
+    avgCntM = 0;
+  }
+  else
+  {
+    avgSetM = avgM;
+    avgCntM = avgM;
+  }
+
+  delay(10);
+}
+
+void SensorLSM9DS1::begin()
+{
+  //reset();
+
+  delay(10);
+
+  setScanAG(AG_Odr119, A_Fs4g | A_LpAuto, G_Fs2000 | G_LpHH);
+  setScanM(M_Odr40 | Mxy_PmMed | M_TmpOn, M_Fs4G, M_Contin, Mz_PmMed);
+
+  avgSetAG  = 6;
+  avgCntAG  = 6;
+
+  avgSetM   = 0;
+  avgCntM   = 0;
+
+
+  delay(10);
+}
+
+// ----------------------------------------------------------------------------
+// Steuerfunktionen, gezielte Prozessorzugriffe und Hilfsfunktionen
+// ----------------------------------------------------------------------------
+//
+/*
+void SensorLSM9DS1::run0()
+{
+  switch(runState)
+  {
+    case rsInit:
+      runState = rsScanAGReq;
+      break;
+
+      // ------------ Accel & Gyro ------------
+
+    case rsScanAGReq:
+      twPtr->recByteReg(AG_Adr, AG_Status, &twiByte);
+      runState = rsScanAGChk;
+      break;
+
+    case rsScanAGChk:
+      if(twiByte.twiStatus != TwStFin)
+        break;
+
+      if((twiByte.value & 0x03) == 0)
+      {
+        waitCnt = 2;
+        runState = rsWaitAG;
+        break;
+      }
+
+      twiByteSeq.len = 12;
+      twPtr->recByteRegSeq(AG_Adr, G_Out, &twiByteSeq);
+      runState = rsFetchAG;
+      break;
+
+    case rsWaitAG:
+      if(waitCnt > 0)
+      {
+        waitCnt--;
+        break;
+      }
+      else
+      {
+        runState = rsScanAGReq;
+      }
+      break;
+
+    case rsFetchAG:
+      if(twiByte.twiStatus != TwStFin)
+        break;
+
+      for(int i = 0; i < 12; i++)
+        tmpDataAG.byteArray[i] = byteArray[i];
+
+      if(avgSetAG > 0)
+      {
+        sumA.x += (int) tmpDataAG.valueAG.A.x;
+        sumA.y += (int) tmpDataAG.valueAG.A.y;
+        sumA.z += (int) tmpDataAG.valueAG.A.z;
+        sumG.x += (int) tmpDataAG.valueAG.G.x;
+        sumG.y += (int) tmpDataAG.valueAG.G.y;
+        sumG.z += (int) tmpDataAG.valueAG.G.z;
+        avgCntAG--;
+
+        if(avgCntAG == 0)
+        {
+          rawDataAG.valueAG.A.x = short (sumA.x / avgSetAG);
+          rawDataAG.valueAG.A.y = short (sumA.y / avgSetAG);
+          rawDataAG.valueAG.A.z = short (sumA.z / avgSetAG);
+          rawDataAG.valueAG.G.x = short (sumG.x / avgSetAG);
+          rawDataAG.valueAG.G.y = short (sumG.y / avgSetAG);
+          rawDataAG.valueAG.G.z = short (sumG.z / avgSetAG);
+
+          sumA.x = sumA.y = sumA.z = sumG.x = sumG.y = sumG.z = 0;
+          avgCntAG = avgSetAG;
+
+          newValueAG = true;
+        }
+      }
+      else
+      {
+        rawDataAG.valueAG.A.x = tmpDataAG.valueAG.A.x;
+        rawDataAG.valueAG.A.y = tmpDataAG.valueAG.A.x;
+        rawDataAG.valueAG.A.z = tmpDataAG.valueAG.A.x;
+        rawDataAG.valueAG.G.x = tmpDataAG.valueAG.A.x;
+        rawDataAG.valueAG.G.y = tmpDataAG.valueAG.A.x;
+        rawDataAG.valueAG.G.z = tmpDataAG.valueAG.A.x;
+        newValueAG = true;
+      }
+
+      runState = rsScanAGReq;
+      break;
+  }
+}
+
+void SensorLSM9DS1::run1()
+{
+  switch(runState)
+  {
+    case rsInit:
+      runState = rsScanAGReq;
+      break;
+
+      // ------------ Accel & Gyro ------------
+
+    case rsScanAGReq:
+      twPtr->recByteReg(AG_Adr, AG_Status, &twiByte);
+      runState = rsScanAGChk;
+      break;
+
+    case rsScanAGChk:
+      if(twiByte.twiStatus != TwStFin)
+        break;
+
+      if((twiByte.value & 0x03) == 0)
+      {
+        waitCnt = 2;
+        runState = rsWaitAG;
+        break;
+      }
+
+      twiByteSeq.len = 12;
+      twPtr->recByteRegSeq(AG_Adr, G_Out, &twiByteSeq);
+      runState = rsFetchAG;
+      break;
+
+    case rsWaitAG:
+      if(waitCnt > 0)
+      {
+        waitCnt--;
+        break;
+      }
+      else
+      {
+        runState = rsScanAGReq;
+      }
+      break;
+
+    case rsFetchAG:
+      if(twiByteSeq.twiStatus != TwStFin)
+        break;
+
+      for(int i = 0; i < 12; i++)
+        rawDataAG.byteArray[i] = byteArray[i];
+
+      newValueAG = true;
+
+      runState = rsScanAGReq;
+      break;
+  }
+}
+
+*/
+
+void SensorLSM9DS1::stop()
+{
+  enableMeasAG = false;
+  enableMeasM = false;
+}
+
+void SensorLSM9DS1::resume()
+{
+  enableMeasAG = true;
+  enableMeasM = true;
+}
+
+void SensorLSM9DS1::run()
+{
+  runStateCntTotal++;
+
+  switch(runState)
+  {
+    // ------------------------------------------------------------------------
+    case rsInit:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsInit]++;
+      runState = rsScanAGReq;
+      timeOutStatusAG = toValueStatusAG;
+      timeOutStatusM  = toValueStatusM;
+      break;
+
+      // ------------ Accel & Gyro ------------
+
+    // ------------------------------------------------------------------------
+    case rsScanAGReq:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsScanAGReq]++;
+      if(!enableMeasAG)
+      {
+        runState = rsScanMReq;
+        break;
+      }
+
+      twPtr->recByteReg(AG_Adr, AG_Status, &twiByte);
+      timeOutTwiStatus = twiStatusCycle / runCycle + 1;
+      runState = rsScanAGChk;
+      break;
+
+    // ------------------------------------------------------------------------
+    case rsWaitAG:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsWaitAG]++;
+      break;
+
+    // ------------------------------------------------------------------------
+    case rsScanAGChk:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsScanAGChk]++;
+      if((twiByte.twiStatus != TwStFin) && ((twiByte.twiStatus & TwStError) == 0))
+      {
+        if(timeOutTwiStatus > 0)
+          timeOutTwiStatus--;
+        else
+        {
+          toCntTwiStatusAG++;
+          runState = rsScanAGReq;
+        }
+        break;
+      }
+
+      if((twiByte.twiStatus & TwStError) != 0)
+      {
+        if(twiByte.twiStatus == TwStAdrNak)
+          errorCntAdrNakAG++;
+        else if(twiByte.twiStatus == TwStDataNak)
+          errorCntDataNakAG++;
+        else
+          errorCntOverAG++;
+
+        runState = rsScanAGReq;
+        break;
+      }
+
+      if((twiByte.value & 0x03) == 0)
+      {
+        if(timeOutStatusAG > 0)
+          timeOutStatusAG--;
+        else
+        {
+          timeOutStatusAG = toValueStatusAG;
+          toCntStatusAG++;
+        }
+        runState = rsScanMReq;    // -> Magnet
+        break;
+      }
+
+      timeOutStatusAG = toValueStatusAG;
+      twiByteSeq.len = 12;
+      twPtr->recByteRegSeq(AG_Adr, G_Out, &twiByteSeq);
+      timeOutTwiDataAG = twiDataCycleAG / runCycle + 1;
+      runState = rsFetchAG;
+      break;
+
+    // ------------------------------------------------------------------------
+    case rsFetchAG:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsFetchAG]++;
+      if((twiByteSeq.twiStatus != TwStFin) && ((twiByte.twiStatus & TwStError) == 0))
+      {
+        if(timeOutTwiDataAG > 0)
+        {
+          timeOutTwiDataAG--;
+          break;
+        }
+      }
+
+      if(((twiByteSeq.twiStatus & TwStError) != 0) || (timeOutTwiDataAG == 0))
+      {
+        if(twiByteSeq.twiStatus == TwStAdrNak)
+          errorCntAdrNakAG++;
+        else if(twiByteSeq.twiStatus == TwStDataNak)
+          errorCntDataNakAG++;
+        else if(twiByteSeq.twiStatus == TwStOverrun)
+          errorCntOverAG++;
+        else
+          toCntTwiDataAG++;
+
+        twiByteSeq.len = 12;
+        twPtr->recByteRegSeq(AG_Adr, G_Out, &twiByteSeq);
+        timeOutTwiDataAG = twiDataCycleAG / runCycle + 1;
+        break;
+      }
+
+      for(int i = 0; i < 12; i++)
+        comDataAG.byteArray[i] = byteArray[i];
+
+      if(avgSetAG > 0)
+      {
+        sumA.x += comDataAG.valueAG.A.x;
+        sumA.y += comDataAG.valueAG.A.y;
+        sumA.z += comDataAG.valueAG.A.z;
+        sumG.x += comDataAG.valueAG.G.x;
+        sumG.y += comDataAG.valueAG.G.y;
+        sumG.z += comDataAG.valueAG.G.z;
+        avgCntAG--;
+
+        if(avgCntAG == 0)
+        {
+          rawDataAG.valueAG.A.x = sumA.x / avgSetAG;
+          rawDataAG.valueAG.A.y = sumA.y / avgSetAG;
+          rawDataAG.valueAG.A.z = sumA.z / avgSetAG;
+          rawDataAG.valueAG.G.x = sumG.x / avgSetAG;
+          rawDataAG.valueAG.G.y = sumG.y / avgSetAG;
+          rawDataAG.valueAG.G.z = sumG.z / avgSetAG;
+          sumA.x = sumA.y = sumA.z = sumG.x = sumG.y = sumG.z = 0;
+          avgCntAG = avgSetAG;
+          newValueAG = true;
+        }
+      }
+      else
+      {
+        rawDataAG.valueAG.A.x = comDataAG.valueAG.A.x;
+        rawDataAG.valueAG.A.y = comDataAG.valueAG.A.y;
+        rawDataAG.valueAG.A.z = comDataAG.valueAG.A.z;
+        rawDataAG.valueAG.G.x = comDataAG.valueAG.G.x;
+        rawDataAG.valueAG.G.y = comDataAG.valueAG.G.y;
+        rawDataAG.valueAG.G.z = comDataAG.valueAG.G.z;
+        newValueAG = true;
+      }
+
+      runState = rsScanAGReq;
+      break;
+
+      // ------------ Magnet ------------
+
+    // ------------------------------------------------------------------------
+    case rsScanMReq:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsScanMReq]++;
+      if(!enableMeasM)
+      {
+        runState = rsScanAGReq;
+        break;
+      }
+
+      twPtr->recByteReg(M_Adr, M_Status, &twiByte);
+      timeOutTwiStatus = twiStatusCycle / runCycle + 1;
+      runState = rsScanMChk;
+      break;
+
+    // ------------------------------------------------------------------------
+    case rsScanMChk:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsScanMChk]++;
+      if((twiByte.twiStatus != TwStFin) && ((twiByte.twiStatus & TwStError) == 0))
+      {
+        if(timeOutTwiStatus > 0)
+          timeOutTwiStatus--;
+        else
+        {
+          toCntTwiStatusM++;
+          runState = rsScanMReq;
+        }
+        break;
+      }
+
+      if((twiByte.twiStatus & TwStError) != 0)
+      {
+        if(twiByte.twiStatus == TwStAdrNak)
+          errorCntAdrNakM++;
+        else if(twiByte.twiStatus == TwStDataNak)
+          errorCntDataNakM++;
+        else
+          errorCntOverM++;
+
+        runState = rsScanAGReq;
+        break;
+      }
+
+      if((twiByte.value & 0x08) == 0)
+      {
+        if(timeOutStatusM > 0)
+          timeOutStatusM--;
+        else
+        {
+          timeOutStatusM = toValueStatusM;
+          toCntStatusM++;
+        }
+        runState = rsScanAGReq;    // -> Accel,Gyro
+        break;
+      }
+
+      timeOutStatusM = toValueStatusM;
+      twiByteSeq.len = 6;
+      twPtr->recByteRegSeq(M_Adr, M_Out, &twiByteSeq);
+      timeOutTwiDataM = twiDataCycleM / runCycle + 1;
+      runState = rsFetchM;
+      break;
+
+    // ------------------------------------------------------------------------
+    case rsFetchM:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsFetchM]++;
+      if((twiByteSeq.twiStatus != TwStFin) && ((twiByte.twiStatus & TwStError) == 0))
+      {
+        if(timeOutTwiDataM > 0)
+        {
+          timeOutTwiDataM--;
+          break;
+        }
+      }
+
+      if( ((twiByteSeq.twiStatus & TwStError) != 0) || (timeOutTwiDataM == 0) )
+      {
+        if(twiByteSeq.twiStatus == TwStAdrNak)
+          errorCntAdrNakM++;
+        else if(twiByteSeq.twiStatus == TwStDataNak)
+          errorCntDataNakM++;
+        else if(twiByteSeq.twiStatus == TwStOverrun)
+          errorCntOverM++;
+        else
+          toCntTwiDataM++;
+
+        twiByteSeq.len = 6;
+        twPtr->recByteRegSeq(AG_Adr, M_Out, &twiByteSeq);
+        timeOutTwiDataM = twiDataCycleM / runCycle + 1;
+        break;
+      }
+
+      for(int i = 0; i < 6; i++)
+        rawDataM.byteArray[i] = byteArray[i];
+
+      if(avgSetM > 0)
+      {
+        sumM.x += rawDataM.valueM.x;
+        sumM.y += rawDataM.valueM.y;
+        sumM.z += rawDataM.valueM.z;
+        avgCntM--;
+
+        if(avgCntM == 0)
+        {
+          rawDataM.valueM.x = sumM.x / avgSetM;
+          rawDataM.valueM.y = sumM.y / avgSetM;
+          rawDataM.valueM.z = sumM.z / avgSetM;
+          sumM.x = sumM.y = sumM.z = 0;
+          avgCntM = avgSetM;
+          newValueM = true;
+        }
+      }
+      else
+      {
+        newValueM = true;
+      }
+
+      runState = rsScanAGReq;
+      break;
+  }
+}
+
+void  SensorLSM9DS1::syncValuesM()
+{
+  newValueM = false;
+}
+
+bool  SensorLSM9DS1::getValuesM(RawDataMPtr rdptr)
+{
+  if(!newValueM) return(false);
+  for(int i = 0; i < 6; i++)
+    rdptr->byteArray[i] = rawDataM.byteArray[i];
+  newValueM = false;
+  return(true);
+}
+
+bool  SensorLSM9DS1::getValuesM(CalValuePtr calPtr)
+{
+  if(!newValueM) return(false);
+
+  calPtr->x = (float) fullScaleM * (float) rawDataM.valueM.x / (float) 32767;
+  calPtr->y = (float) fullScaleM * (float) rawDataM.valueM.y / (float) 32767;
+  calPtr->z = (float) fullScaleM * (float) rawDataM.valueM.z / (float) 32767;
+
+  newValueM = false;
+  return(true);
+}
+
+void  SensorLSM9DS1::syncValuesAG()
+{
+  newValueAG = false;
+}
+
+bool  SensorLSM9DS1::getValuesAG(RawDataAGPtr rdptr)
+{
+  if(!newValueAG) return(false);
+  for(int i = 0; i < 12; i++)
+    rdptr->byteArray[i] = rawDataAG.byteArray[i];
+  newValueAG = false;
+  return(true);
+}
+
+bool  SensorLSM9DS1::getValuesAG(CalValueAGPtr calPtr)
+{
+  if(!newValueAG) return(false);
+
+  calPtr->G.x = (float) fullScaleG * (float) rawDataAG.valueAG.G.x / (float) 32767;
+  calPtr->G.y = (float) fullScaleG * (float) rawDataAG.valueAG.G.y / (float) 32767;
+  calPtr->G.z = (float) fullScaleG * (float) rawDataAG.valueAG.G.z / (float) 32767;
+
+  calPtr->A.x = (float) fullScaleA * (float) rawDataAG.valueAG.A.x / (float) 32767;
+  calPtr->A.y = (float) fullScaleA * (float) rawDataAG.valueAG.A.y / (float) 32767;
+  calPtr->A.z = (float) fullScaleA * (float) rawDataAG.valueAG.A.z / (float) 32767;
+
+  newValueAG = false;
+  return(true);
+}
+
+bool  SensorLSM9DS1::getAvgValuesAG(CalValueAGPtr calPtr)
+{
+  if(!newValueAG) return(false);
+  newValueAG = false;
+
+  if(avgSetAG > 0)
+  {
+    sumA.x += rawDataAG.valueAG.A.x;
+    sumA.y += rawDataAG.valueAG.A.y;
+    sumA.z += rawDataAG.valueAG.A.z;
+    sumG.x += rawDataAG.valueAG.G.x;
+    sumG.y += rawDataAG.valueAG.G.y;
+    sumG.z += rawDataAG.valueAG.G.z;
+    avgCntAG--;
+
+    if(avgCntAG > 0) return(false);
+
+    calPtr->G.x = (float) fullScaleG * (float) (sumG.x / avgSetAG) / (float) 32767;
+    calPtr->G.y = (float) fullScaleG * (float) (sumG.y / avgSetAG) / (float) 32767;
+    calPtr->G.z = (float) fullScaleG * (float) (sumG.z / avgSetAG) / (float) 32767;
+
+    calPtr->A.x = (float) fullScaleA * (float) (sumA.x / avgSetAG) / (float) 32767;
+    calPtr->A.y = (float) fullScaleA * (float) (sumA.y / avgSetAG) / (float) 32767;
+    calPtr->A.z = (float) fullScaleA * (float) (sumA.z / avgSetAG) / (float) 32767;
+
+    avgCntAG = avgSetAG;
+    return(true);
+  }
+
+  calPtr->G.x = (float) fullScaleG * (float) rawDataAG.valueAG.G.x / (float) 32767;
+  calPtr->G.y = (float) fullScaleG * (float) rawDataAG.valueAG.G.y / (float) 32767;
+  calPtr->G.z = (float) fullScaleG * (float) rawDataAG.valueAG.G.z / (float) 32767;
+
+  calPtr->A.x = (float) fullScaleA * (float) rawDataAG.valueAG.A.x / (float) 32767;
+  calPtr->A.y = (float) fullScaleA * (float) rawDataAG.valueAG.A.y / (float) 32767;
+  calPtr->A.z = (float) fullScaleA * (float) rawDataAG.valueAG.A.z / (float) 32767;
+
+  return(true);
+}
+
+
+
+
+// ----------------------------------------------------------------------------
+// Ereignisbearbeitung und Interrupts
+// ----------------------------------------------------------------------------
+//
+
+// ----------------------------------------------------------------------------
+//                      D e b u g - H i l f e n
+// ----------------------------------------------------------------------------
+//
+dword SensorLSM9DS1::debGetDword(int code)
+{
+  dword retv;
+
+
+  switch(code)
+  {
+    case 1:
+      retv = toValueStatusAG;
+      break;
+
+    case 2:
+      retv = toValueStatusM;
+      break;
+
+    default:
+      retv = 0;
+      break;
+  }
+  return(retv);
+}
+
+
+dword SensorLSM9DS1::debGetRunState(int code)
+{
+  if(0 <= code < 9)
+    return(runStateCntArray[code]);
+  else
+    return(0);
+}
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SensorLSM9DS1/SensorLSM9DS1.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SensorLSM9DS1/SensorLSM9DS1.h
new file mode 100644
index 0000000000000000000000000000000000000000..63cb831c96688b30a85397927a14a52dcb6a15f2
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SensorLSM9DS1/SensorLSM9DS1.h
@@ -0,0 +1,363 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   SensorLSM9DS1.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#ifndef SENSORLSM9DS1_H
+#define SENSORLSM9DS1_H
+
+#include "Arduino.h"
+#include "arduinoDefs.h"
+#include "IntrfTw.h"
+
+// ----------------------------------------------------------------------------
+
+// ------------------------ Acceleration and Gyroscope -------
+#define AG_Adr    0x6B
+// ------------------------ Acceleration and Gyroscope -------
+#define AG_Id     0x0F
+#define AG_Ctrl1  0x10
+#define G_Out     0x18
+#define AG_Ctrl6  0x20
+#define AG_Ctrl8  0x22
+#define AG_Status 0x27
+
+#define AG_Rate(x)      (x << 5)
+#define AG_Odr14_9      0x20
+#define AG_Odr59_5      0x40
+#define AG_Odr119       0x60
+#define AG_Odr238       0x80
+#define AG_Odr476       0xA0
+#define AG_Odr952       0xC0
+
+typedef enum _FreqAG
+{
+  FreqAG_OFF  = 0xFF,
+  FreqAG14_9  = AG_Odr14_9,
+  FreqAG59_5  = AG_Odr59_5,
+  FreqAG119   = AG_Odr119,
+  FreqAG238   = AG_Odr238,
+  FreqAG476   = AG_Odr476,
+  FreqAG952   = AG_Odr952
+} FreqAG;
+
+#define AG_FullScale(x) (x << 3)
+#define A_Fs2g          0x00
+#define A_Fs4g          0x10
+#define A_Fs8g          0x18
+#define A_Fs16g         0x08
+#define G_Fs245         0x00
+#define G_Fs2000        0x18
+#define G_Fs500         0x08
+
+typedef enum _MaxA
+{
+  MaxAcc2g    = A_Fs2g,
+  MaxAcc4g    = A_Fs4g,
+  MaxAcc8g    = A_Fs8g,
+  MaxAcc16g   = A_Fs16g
+} MaxA;
+
+typedef enum _MaxG
+{
+  MaxGyro245dps   = G_Fs245,
+  MaxGyro500dps   = G_Fs500,
+  MaxGyro2000dps  = G_Fs2000
+} MaxG;
+
+#define AG_LowPass(x)   (x)
+#define A_LpAuto        0x00
+#define A_Lp50          0x07
+#define A_Lp105         0x06
+#define A_Lp211         0x05
+#define A_Lp408         0x04
+#define G_LpLL          0x00
+#define G_LpLH          0x01
+#define G_LpHL          0x02
+#define G_LpHH          0x03
+
+// ------------------------ Magnetic Field -------
+#define M_Adr     0x1E
+// ------------------------ Magnetic Field -------
+#define M_Id      0x0F
+#define M_Ctrl1   0x20
+#define M_Ctrl2   0x21
+#define M_Ctrl3   0x22
+#define M_Ctrl4   0x23
+#define M_Ctrl5   0x24
+#define M_Status  0x27
+#define M_Out     0x28
+
+// Control 1
+#define M_Rate(x)       (x << 2)
+#define M_Odr0_625      0x00
+#define M_Odr1_25       0x04
+#define M_Odr2_5        0x08
+#define M_Odr5          0x0C
+#define M_Odr10         0x10
+#define M_Odr20         0x14
+#define M_Odr40         0x18
+#define M_Odr80         0x1C
+
+typedef enum _FreqM
+{
+  FreqM_OFF   = 0xFF,
+  FreqM0_625  = M_Odr0_625,
+  FreqM1_25   = M_Odr1_25,
+  FreqM2_5    = M_Odr2_5,
+  FreqM5      = M_Odr5,
+  FreqM10     = M_Odr10,
+  FreqM20     = M_Odr20,
+  FreqM40     = M_Odr40,
+  FreqM80     = M_Odr80
+} FreqM;
+
+#define M_Temp(x)       (x << 7)
+#define M_TmpOn         0x80
+#define M_TmpOff        0x00
+
+#define Mxy_Power(x)    (x << 6)
+#define Mxy_PmLow       0x00
+#define Mxy_PmMed       0x20
+#define Mxy_PmHigh      0x40
+#define Mxy_PmUhigh     0x60
+
+// Control 2
+#define M_FullScale(x)  (x << 5)
+#define M_Fs4G          0x00
+#define M_Fs8G          0x20
+#define M_Fs12G         0x40
+#define M_Fs16G         0x60
+
+typedef enum _MaxM
+{
+  MaxMag4G      = M_Fs4G,
+  MaxMag8G      = M_Fs8G,
+  MaxMag12G     = M_Fs12G,
+  MaxMag16G     = M_Fs16G
+} MaxM;
+
+
+// Control 3
+#define M_OpMode(x)     (x)
+#define M_Contin        0x00
+#define M_Single        0x01
+#define M_Down          0x10
+
+// Control 4
+#define Mz_Power(x)    (x << 2)
+#define Mz_PmLow       0x00
+#define Mz_PmMed       0x04
+#define Mz_PmHigh      0x08
+#define Mz_PmUhigh     0x0C
+
+
+typedef enum _RunState
+{
+  rsInit,
+  rsScanAGReq,
+  rsWaitAG,
+  rsScanAGChk,
+  rsFetchAG,
+  rsScanMReq,
+  rsScanMChk,
+  rsFetchM
+} RunState;
+
+#define NrOfRunStates 8
+
+typedef struct _RawValue
+{
+  short int   x;
+  short int   y;
+  short int   z;
+} RawValue;
+
+typedef struct _SumValue
+{
+  int   x;
+  int   y;
+  int   z;
+} SumValue, *SumValuePtr;
+
+typedef struct _RawValueAG
+{
+  RawValue  G;
+  RawValue  A;
+} RawValueAG;
+
+typedef union _RawDataAG
+{
+  byte        byteArray[12];
+  RawValueAG  valueAG;
+} RawDataAG, *RawDataAGPtr;
+
+typedef union _RawDataM
+{
+  byte        byteArray[6];
+  RawValue    valueM;
+} RawDataM, *RawDataMPtr;
+
+typedef struct _CalValue
+{
+  float   x;
+  float   y;
+  float   z;
+} CalValue, *CalValuePtr;
+
+typedef struct _CalValueAG
+{
+  CalValue  G;
+  CalValue  A;
+} CalValueAG, *CalValueAGPtr;
+
+typedef struct _SensorErrors
+{
+
+} SensorErrors, *SensorErrorsPtr;
+
+class SensorLSM9DS1
+{
+private:
+  // --------------------------------------------------------------------------
+  // Lokale Daten und Funktionen
+  // --------------------------------------------------------------------------
+  //
+  IntrfTw     *twPtr;
+  TwiByte     twiByte;
+  TwiByteSeq  twiByteSeq;
+  byte        byteArray[12];
+
+  bool        enableMeasAG;
+  bool        newValueAG;
+  RawDataAG   rawDataAG;
+  RawDataAG   comDataAG;
+
+  bool        enableMeasM;
+  bool        newValueM;
+  RawDataM    rawDataM;
+
+  int         fullScaleA;
+  int         fullScaleG;
+  int         fullScaleM;
+
+  SumValue    sumA;
+  SumValue    sumG;
+  int         avgSetAG;
+  int         avgCntAG;
+
+  SumValue    sumM;
+  int         avgSetM;
+  int         avgCntM;
+
+  dword       timeOutTwiStatus;
+  dword       timeOutTwiDataAG;
+  dword       timeOutTwiDataM;
+
+  dword       timeOutStatusAG;
+  dword       toValueStatusAG;
+  dword       timeOutStatusM;
+  dword       toValueStatusM;
+
+  int         twiCycle;
+  int         twiStatusCycle;
+  int         twiDataCycleAG;
+  int         twiDataCycleM;
+
+  RunState    runState;
+  int         waitCnt;
+  int         runCycle;
+
+  void setTimeOutValues(FreqAG fAG, FreqM fM);
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen der Basis-Klasse
+  // --------------------------------------------------------------------------
+
+  SensorLSM9DS1(IntrfTw *refI2C, int inRunCycle);
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  int resetAG();
+  int resetM();
+  int reset();
+
+  void setScanAG(byte scValueAG, byte scValueA, byte scValueG);
+  // Messparameter für Accel und Gyro
+  // scValueAG = Abtastrate
+  // scValueA  = Vollausschlag und Tiefpass für Beschleunigung
+  // scValueB  = Vollausschlag und Tiefpass für Gyrometer
+
+  void setScanM(byte scValue1, byte scValue2, byte scValue3, byte scValue4);
+  // Messparameter für Magnetfeld
+  // scValue1 = Abtastrate, Temperaturkompensation und XY-Powermode
+  // scValue2 = Vollausschlag
+  // scValue3 = Betriebsart
+  // scValue4 = Z-Powermode
+
+
+  void begin(FreqAG freqAG, int avgAG, MaxA maxA, MaxG maxG, FreqM freqM, int avgM, MaxM maxM);
+  void begin();
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+  void run();
+  void run0();
+  void run1();
+  void stop();
+  void resume();
+
+  // --------------------------------------------------------------------------
+  // Datenaustausch
+  // --------------------------------------------------------------------------
+  //
+  dword       errorCntOverAG;
+  dword       errorCntAdrNakAG;
+  dword       errorCntDataNakAG;
+
+  dword       errorCntOverM;
+  dword       errorCntAdrNakM;
+  dword       errorCntDataNakM;
+
+  dword       toCntTwiStatusAG;
+  dword       toCntTwiStatusM;
+  dword       toCntTwiDataAG;
+  dword       toCntTwiDataM;
+  dword       toCntStatusAG;
+  dword       toCntStatusM;
+
+  void  syncValuesAG();
+  bool  getValuesAG(RawDataAGPtr rdptr);
+  bool  getValuesAG(CalValueAGPtr calPtr);
+  bool  getAvgValuesAG(CalValueAGPtr calPtr);
+  void  syncValuesM();
+  bool  getValuesM(RawDataMPtr rdptr);
+  bool  getValuesM(CalValuePtr calPtr);
+
+  // ----------------------------------------------------------------------------
+  // Ereignisbearbeitung und Interrupts
+  // ----------------------------------------------------------------------------
+  //
+
+  // ----------------------------------------------------------------------------
+  //                      D e b u g - H i l f e n
+  // ----------------------------------------------------------------------------
+  //
+  dword runStateCntArray[NrOfRunStates];
+  dword runStateCntTotal;
+  dword debGetDword(int code);
+  dword debGetRunState(int code);
+
+};
+
+#endif // SENSORLSM9DS1_H
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SensorLSM9DS1/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SensorLSM9DS1/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..6432e5ccb8930ce40504aa4f03e463f2df00fac0
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SensorLSM9DS1/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "SensorLSM9DS1",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapComDue/SoaapComDue.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapComDue/SoaapComDue.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b24e6f4f90c3ecc1588b5af5dde88b4b922e8ee7
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapComDue/SoaapComDue.cpp
@@ -0,0 +1,212 @@
+//-----------------------------------------------------------------------------
+// Thema:   Steuerung optischer und akustischer Ausgaben für Perfomer
+// Datei:   SoaapComDue.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.hs-hannover.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   01.April 2022
+//
+// Diese Bibliothek (Klassse) enthält diverse Ressourcen zum Zugriff auf den
+// SOAAP-Master über eine serielle Schnittstelle des Arduino DUE.
+//
+
+#include "SoaapComDue.h"
+
+#define next(x) nextState = &SoaapComDue::x
+
+// ----------------------------------------------------------------------------
+// Konstruktoren und Initialisierungen
+// ----------------------------------------------------------------------------
+//
+SoaapComDue::SoaapComDue(USARTClass *serial, SoaapMsg *soaMsg)
+{
+  pCom = serial;
+  pMsg = soaMsg;
+  nextState = &SoaapComDue::runInit;
+
+  nrBytesIn = 0;
+  serInIdx = 0;
+  serInChar = 0;
+  serInCount = 0;
+  slaveAdr = 0;
+  slaveArea = 0;
+  appId = (SoaapApId) 0;
+
+  measIdx = 0;
+  nrMeas = 0;
+  resMeas = 0;
+  slaveIdx = 0;
+  anyNewVal = false;
+}
+
+// --------------------------------------------------------------------------
+// lokale Methoden (Zustandsmaschine)
+// --------------------------------------------------------------------------
+//
+void SoaapComDue::runInit()
+{
+  next(runWaitMsg);
+}
+
+void SoaapComDue::runWaitMsg()
+{
+  nrBytesIn = pCom->available();
+  // Anzahl der über <serial> eingetroffenen Bytes (Zeichen)
+
+  if(nrBytesIn < 1) return;
+  // Beim nächsten Takt wieder in diesen Zustand, wenn kein Zeichen da
+
+  for(serInIdx = 0; serInIdx < nrBytesIn; serInIdx++)
+  {                               // Suchen nach Startzeichen
+    serInChar = pCom->read();
+    if(serInChar < 0x20) break;
+  }
+
+  if(serInIdx == nrBytesIn) return;
+  // Beim nächsten Takt wieder in diesen Zustand, wenn Startzeichen nicht dabei
+
+  slaveArea = serInChar & 0x1F;   // Bereich auskodieren
+
+  serInIdx = 0;     // Index neu setzen für Inhaltszuordnung
+  next(runHeader);
+  // Beim nächsten Takt zum Zustand <runHeader>
+
+}
+
+void SoaapComDue::runHeader()
+{
+    nrBytesIn = pCom->available();
+    // Anzahl der über Serial1 eingetroffenen Bytes (Zeichen)
+
+    if(nrBytesIn < 1) return;
+    // Beim nächsten Takt wieder in diesen Zustand, wenn kein Zeichen da
+
+    serInChar = pCom->read();
+    // einzelnes Zeichen lesen
+
+    if(serInIdx == 0)             // nach der Area folgt die Slaveadresse
+    {
+      slaveAdr = serInChar & 0x1F;               // 1 - 31
+      slaveIdx = slaveAdr;        // vorläufig lineare Adress/Index-Zuordnung
+      serInIdx++;
+      // Beim nächsten Takt wieder in diesen Zustand
+    }
+    else                     // und dann die Anwendungskennung
+    {
+      appId = (SoaapApId) serInChar;
+      measIdx = 0;                      // Index für Messwertunterscheidung
+      serInIdx = 0;                     // Index für Messwertaufbau
+      nrMeas = pMsg->measCnt(appId);   // Anzahl Messwerte im Telegramm
+      resMeas = pMsg->measRes(appId);  // Auflösung der Messwerte in Zeichen
+      next(runValues);
+      // Beim nächsten Takt zum Zustand <runValues>
+    }
+}
+
+void SoaapComDue::runValues()
+{
+  int i;
+
+  do
+  {
+    nrBytesIn = pCom->available();
+    // Anzahl der über Serial1 eingetroffenen Bytes (Zeichen)
+
+    if(nrBytesIn < 1) return;
+    // Beim nächsten Takt wieder in diesen Zustand, wenn kein Zeichen da
+
+    for(i = 0; i < nrBytesIn; i++)
+    {
+      tmpBuffer[serInIdx++] = pCom->read();
+      // einzelnes Zeichen lesen
+
+      if(serInIdx == resMeas) break;
+      // Alle Zeichen vom Messwert da, also raus
+    }
+
+    if(serInIdx < resMeas) return;
+    // Wenn noch nicht ale Zeichen vom Messwert erfasst, dann von vorn
+
+    slValList[slaveIdx].measList[measIdx++] = pMsg->asc2meas(tmpBuffer);
+    // Zeichenkette in Messwert wandeln und speichern
+
+    if(measIdx == nrMeas) break;
+    // Falls mehr als ein Telegramm eingetroffen ist
+    // muss hier ein Ausstieg erfolgen
+
+    serInIdx = 0;   // Nächste Zeichenfolge
+  }
+  while (pCom->available() > 0);
+  // Die Taktzeit der Zustandsmaschine ist größer, als die
+  // Übertragungszeit von einem Zeichen.
+  // Deshalb werden in einem Zustandstakt alle inzwischen eingetroffenen
+  // Zeichen bearbeitet.
+
+  if(measIdx < nrMeas) return;
+  // Im Zustand bleiben, bis alle Messwerte gewandelt sind
+
+  slValList[slaveIdx].newVal = true;
+  anyNewVal = true;
+
+  next(runWaitMsg);
+}
+
+// --------------------------------------------------------------------------
+// lokale Methoden (Hilfsfunktionen)
+// --------------------------------------------------------------------------
+//
+
+// Erster Slave (kleinste Adresse) mit Daten
+//
+int   SoaapComDue::getSlDataAvail()
+{
+  for(int i = 1; i <= ScdNrOfSlaves; i++)
+    if(slValList[i].newVal)
+      return(i);
+  return(0);
+}
+
+// ----------------------------------------------------------------------------
+// Anwenderschnittstelle (Funktionen/Methoden)
+// ----------------------------------------------------------------------------
+//
+
+// (zyklischer) Aufruf zum Ablauf
+//
+void SoaapComDue::run()
+{
+  if(nextState != NULL)
+    (this->*nextState)();
+}
+
+// Daten von irgendeinem Slave verfügbar
+//
+int   SoaapComDue::anyDataAvail()
+{
+  if(!anyNewVal) return(0);
+  else return(getSlDataAvail());
+}
+
+// Daten von bestimmtem Slave verfügbar
+//
+bool  SoaapComDue::slDataAvail(int slAdr)
+{
+  return(slValList[getSlIdxAdr(slAdr)].newVal);
+}
+
+// Daten von einem bestimmten Slave abholen
+//
+int   SoaapComDue::getData(int slAdr, pMeasValues pMeas)
+{
+  int slIdx = getSlIdxAdr(slAdr);
+  for(int i = 0; i < 8; i++)
+    pMeas->defShort[i] = slValList[slIdx].measList[i];
+  slValList[slIdx].newVal = false;
+  anyNewVal = false;
+  for(int i = 1; i <= ScdNrOfSlaves; i++)
+    if(slValList[i].newVal) anyNewVal = true;
+  return(0);
+}
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapComDue/SoaapComDue.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapComDue/SoaapComDue.h
new file mode 100644
index 0000000000000000000000000000000000000000..990faa3710733dedfd4819f254c11b8900f15d65
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapComDue/SoaapComDue.h
@@ -0,0 +1,132 @@
+//-----------------------------------------------------------------------------
+// Thema:   Steuerung optischer und akustischer Ausgaben für Perfomer
+// Datei:   SoaapComDue.h
+// Editor:  Robert Patzke
+// URI/URL: www.hs-hannover.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   01.April 2022
+//
+// Diese Bibliothek (Klassse) enthält diverse Ressourcen zum Zugriff auf den
+// SOAAP-BLE-Master über eine serielle Schnittstelle des Arduino DUE.
+//
+
+#ifndef SoaapComDue_h
+#define SoaapComDue_h
+
+#define ScdNrOfSlaves     6
+
+#include "USARTClass.h"
+#include "SoaapMsg.h"
+
+#ifndef byte
+#define byte unsigned char
+#endif
+
+
+class SoaapComDue
+{
+public:
+  // --------------------------------------------------------------------------
+  // Konstruktoren und Initialisierungen
+  // --------------------------------------------------------------------------
+  //
+  SoaapComDue(USARTClass *serial, SoaapMsg *soaMsg);
+
+  // --------------------------------------------------------------------------
+  // öffentliche Datentypen
+  // --------------------------------------------------------------------------
+  //
+  typedef union
+  {
+    short   defShort[9];
+    short   maxShort[13];
+    float   maxFloat[6];
+  } MeasValues, *pMeasValues;
+
+private:
+  // --------------------------------------------------------------------------
+  // lokale Datentypen
+  // --------------------------------------------------------------------------
+  //
+
+  typedef void (SoaapComDue::*CbVector)(void);
+  typedef struct
+  {
+    int         slaveArea;
+    int         slaveAdr;
+    SoaapApId   apId;
+    bool        newVal;
+    short       measList[13];
+  } SlaveValues, *pSlaveValues;
+
+private:
+  // --------------------------------------------------------------------------
+  // lokale Variablen
+  // --------------------------------------------------------------------------
+  //
+  USARTClass *pCom;
+  SoaapMsg   *pMsg;
+  CbVector    nextState;
+
+  int         nrBytesIn;          // Antahl aktuell empfangener Zeichen
+  int         serInCount;         // Zähler für empfangene Zeichen
+  int         serInIdx;           // Index für besonderes Zeichen
+  byte        serInChar;          // Einzelnes empfangenes Zeichen
+  int         slaveAdr;           // Adresse des Soaap-Slave
+  int         slaveArea;          // Quellennetzwerk-Info, Bereich, o.ä.
+  SoaapApId   appId;              // Anwendungskennung, Datentyp, o.ä.
+
+  int         measIdx;            // Index für den aktuellen Messwert
+  int         nrMeas;             // Anzahl der Messwerte im Telegramm
+  int         resMeas;            // Auflösung der Messwerte in Zeichen
+  bool        anyNewVal;          // Neuer Wert von beliebigem Slave
+
+  SlaveValues slValList[ScdNrOfSlaves + 1]; // Vorläufige Liste aller Messwerte
+  int         slaveIdx;           // Index für lokale Slaveverwaltung
+
+  byte        tmpBuffer[128];     // Zwischenspeicher für empfangene Zeichen
+
+  // --------------------------------------------------------------------------
+  // Inline-Methoden
+  // --------------------------------------------------------------------------
+  //
+
+  // Index von Slave best. Adresse für Datenliste
+  //
+  int  getSlIdxAdr(int slAdr)
+  {
+    // Zur Zeit sind Index und Slaveadresse identisch (1-6)
+    // Das wir später bei freier Zuordnung angepasst
+    return(slAdr);
+  }
+
+  // --------------------------------------------------------------------------
+  // lokale Methoden (Zustandsmaschine)
+  // --------------------------------------------------------------------------
+  //
+  void runInit();
+  void runWaitMsg();
+  void runHeader();
+  void runValues();
+
+  // --------------------------------------------------------------------------
+  // lokale Methoden (Hilfsfunktionen)
+  // --------------------------------------------------------------------------
+  //
+  int   getSlDataAvail();
+
+public:
+  // --------------------------------------------------------------------------
+  // Anwenderschnittstelle (Funktionen)
+  // --------------------------------------------------------------------------
+  //
+  void run();                     // (zyklischer) Aufruf zum Ablauf
+  int   anyDataAvail();           // Daten von irgendeinem Slave verfügbar
+  bool  slDataAvail(int slAdr);   // Daten von bestimmtem Slave verfügbar
+  int   getData(int slAdr, pMeasValues pMeas);
+  // Daten von einem bestimmten Slave abholen
+
+};
+
+#endif // SoaapComDue_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapComDue/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapComDue/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..d2cf1518adba3b9cb3a57d0eca8963fbceca345c
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapComDue/library.json
@@ -0,0 +1,4 @@
+{
+    "name": "SoaapComDue",
+    "version": "0.0.0+20220804174235"
+  }
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapMsg/SoaapMsg.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapMsg/SoaapMsg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0f3f29704b3b1d5e02528071b0527804940435bd
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapMsg/SoaapMsg.cpp
@@ -0,0 +1,159 @@
+//-----------------------------------------------------------------------------
+// Thema:   Steuerung optischer und akustischer Ausgaben für Perfomer
+// Datei:   SoaapMsg.h
+// Editor:  Robert Patzke
+// URI/URL: www.hs-hannover.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   18. März 2022
+//
+// Diese Bibliothek (Klassse) enthält diverse Ressourcen zur Generierung
+// von Meldungen und Telegrammen, die im Rahmen des SOAAP-Projektes
+// eingesetzt werden.
+//
+
+#include "SoaapMsg.h"
+
+// ----------------------------------------------------------------------------
+// Konstruktoren und Initialisierungen
+// ----------------------------------------------------------------------------
+//
+SoaapMsg::SoaapMsg()
+{
+
+}
+
+// ----------------------------------------------------------------------------
+// Anwenderschnittstelle (Funktionen)
+// ----------------------------------------------------------------------------
+//
+
+// Erstellen eines ASCII-Telegramms zum Übertragen der Messwerte
+//
+int   SoaapMsg::getMsgA(int area, int slvNr, SoaapApId appId, char *dest, byte *meas)
+{
+  int   msgIdx = 0;
+  int   measIdx;
+  int   measLen;
+  byte  measByte;
+  char  measChar;
+
+  dest[msgIdx++]  = (char) (area | 0x10);
+  dest[msgIdx++]  = (char) (slvNr | 0x60);
+  dest[msgIdx++]  = (char) (appId);
+
+  switch(appId)
+  {
+    case saiDefaultMeas:
+      measLen = 18;
+      break;
+
+    case saiDefaultMeasCtrl:
+      measLen = 18;
+      break;
+
+   case saiMaximalMeas:
+      measLen = 26;
+      break;
+  }
+
+  for(measIdx = 0; measIdx < measLen; measIdx++)
+  {
+    measByte = meas[measIdx];
+
+    // Erst das niederwertige Nibble als Hex-Ascii eintragen
+    //
+    measChar = (measByte & 0x0F) | 0x30;
+    if (measChar > 0x39) measChar += 7;
+    dest[msgIdx++] = measChar;
+
+    // dann das höherwertige Nibble
+    //
+    measChar = (measByte >> 4) | 0x30;
+    if (measChar > 0x39) measChar += 7;
+    dest[msgIdx++] = measChar;
+  }
+
+  if(appId == saiDefaultMeasCtrl)
+  {
+    measByte = meas[20];
+
+    // Erst das niederwertige Nibble als Hex-Ascii eintragen
+    //
+    measChar = (measByte & 0x0F) | 0x30;
+    if (measChar > 0x39) measChar += 7;
+    dest[msgIdx++] = measChar;
+
+    // dann das höherwertige Nibble
+    //
+    measChar = (measByte >> 4) | 0x30;
+    if (measChar > 0x39) measChar += 7;
+    dest[msgIdx++] = measChar;
+  }
+
+  dest[msgIdx] = '\0';
+  return(msgIdx);
+}
+
+// Umwandeln eines Messwert aus ASCII-Telegramm in Integer
+//
+short  SoaapMsg::asc2meas(byte *ascList)
+{
+  unsigned short retv;
+
+  retv = getVal(ascList[0]);
+  retv += getVal(ascList[1]) << 4;
+  retv += getVal(ascList[2]) << 8;
+  retv += getVal(ascList[3]) << 12;
+
+  return((short) retv);
+}
+
+// Auflösung der Messwerte in Zeichen (Bytes)
+//
+int   SoaapMsg::measRes(SoaapApId appId)
+{
+  int retv = 0;
+
+  switch(appId)
+  {
+    case saiDefaultMeas:
+      retv = 4;
+      break;
+
+    case saiDefaultMeasCtrl:
+      retv = 4;
+      break;
+
+    case saiMaximalMeas:
+      retv = 4;
+      break;
+  }
+
+  return(retv);
+}
+
+// Anzahl der Messwerte im Telegramm
+//
+int   SoaapMsg::measCnt(SoaapApId appId)
+{
+  int retv = 0;
+
+  switch(appId)
+  {
+    case saiDefaultMeas:
+      retv = 9;
+      break;
+
+    case saiDefaultMeasCtrl:
+      retv = 9;
+      break;
+
+    case saiMaximalMeas:
+      retv = 13;
+      break;
+  }
+
+  return(retv);
+}
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapMsg/SoaapMsg.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapMsg/SoaapMsg.h
new file mode 100644
index 0000000000000000000000000000000000000000..20df5732381c353181e02ce74cf3cf193789c5a0
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapMsg/SoaapMsg.h
@@ -0,0 +1,73 @@
+//-----------------------------------------------------------------------------
+// Thema:   Steuerung optischer und akustischer Ausgaben für Perfomer
+// Datei:   SoaapMsg.h
+// Editor:  Robert Patzke
+// URI/URL: www.hs-hannover.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   18. März 2022
+//
+// Diese Bibliothek (Klassse) enthält diverse Ressourcen zur Generierung
+// von Meldungen und Telegrammen, die im Rahmen des SOAAP-Projektes
+// eingesetzt werden.
+//
+
+#ifndef SoaapMsg_h
+#define SoaapMsg_h
+
+#include "arduinoDefs.h"
+
+typedef enum
+{
+  saiDefaultMeas = 0x68,
+  saiMaximalMeas = 0x69,
+  saiDefaultMeasCtrl = 0x6A
+} SoaapApId;
+
+class SoaapMsg
+{
+public:
+  // --------------------------------------------------------------------------
+  // Konstruktoren und Initialisierungen
+  // --------------------------------------------------------------------------
+  //
+  SoaapMsg();
+
+private:
+  // --------------------------------------------------------------------------
+  // lokale Variablen
+  // --------------------------------------------------------------------------
+  //
+
+  // --------------------------------------------------------------------------
+  // Inline-Methoden
+  // --------------------------------------------------------------------------
+  //
+  int getVal(char hexAsc)
+  {
+    if(hexAsc < 0x39) return(hexAsc - 0x30);
+    else return(hexAsc - 0x37);
+  }
+
+
+public:
+  // --------------------------------------------------------------------------
+  // Anwenderschnittstelle (Funktionen)
+  // --------------------------------------------------------------------------
+  //
+
+  int   getMsgA(int area, int slvNr, SoaapApId appId, char *dest, byte *meas);
+  // Erstellen eines ASCII-Telegramms zum Übertragen der Messwerte
+
+  short asc2meas(byte *ascList);
+  // Umwandeln eines Messwert aus ASCII-Telegramm in Integer
+
+  int   measRes(SoaapApId appId);
+  // Auflösung der Messwerte in Zeichen (Bytes)
+
+  int   measCnt(SoaapApId appId);
+  // Anzahl der Messwerte
+
+};
+
+#endif // SoaapMsg_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapMsg/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapMsg/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..86be2b961f0f6f6dae95ed22fadc55f7a1c58826
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/SoaapMsg/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "SoaapMsg",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/StateMachine/StateMachine.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/StateMachine/StateMachine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9c1f78d3b83eabbc30463d78e693b470e345e140
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/StateMachine/StateMachine.cpp
@@ -0,0 +1,480 @@
+// ---------------------------------------------------------------------------
+// File:        StateMachine.cpp
+// Editors:     Robert Patzke,
+// Start:       07. February 2018
+// Last change: 22. February 2021
+// URI/URL:     www.mfp-portal.de, homeautomation.x-api.de
+// Licence:     Creative Commons CC-BY-SA
+// ---------------------------------------------------------------------------
+//
+
+#include "StateMachine.h"
+
+// ---------------------------------------------------------------------------
+// Constructors and initialisations
+// ---------------------------------------------------------------------------
+//
+
+int  StateMachine::StateMachine_InstCounter;
+
+StateMachine::StateMachine(){;} // @suppress("Class members should be properly initialized")
+
+StateMachine::StateMachine(StatePtr firstState, StatePtr anyState, int cycle)
+{
+  begin(firstState, anyState, cycle);
+}
+
+StateMachine::StateMachine(StatePtr firstState, StatePtr anyState, int cycle, MicsecFuPtr micsecFu)
+{
+  begin(firstState, anyState, cycle, micsecFu);
+}
+
+void StateMachine::begin(StatePtr firstState, StatePtr anyState, int cycle)
+{
+  begin(firstState, anyState, cycle, NULL);
+}
+
+void StateMachine::begin(StatePtr firstState, StatePtr anyState, int cycle, MicsecFuPtr micsecFu)
+{
+  nextState     = firstState;
+  doAlways      = anyState;
+  cycleTime     = cycle;
+  frequency     = 1000 / cycle;
+  delay         = 0;
+  delaySet      = 0;
+  repeatDelay   = false;
+  userStatus    = 0;
+
+  StateMachine_InstCounter++;
+  instNumber    = StateMachine_InstCounter;
+  micsFuPtr     = micsecFu;
+}
+
+// ---------------------------------------------------------------------------
+// run      State enter function, has to be cyclic called
+// ---------------------------------------------------------------------------
+//
+void StateMachine::run()
+{
+  unsigned long startMics = 0, diffMics;
+
+  runCounter++;
+
+  if(timeOutCounter > 0)
+    timeOutCounter--;
+
+  if(timeMeasureOn)
+    timeMeasureCounter++;
+
+  if(doAlways != NULL)
+    doAlways();
+
+  if(delay > 0)
+  {
+    delay--;
+    return;
+  }
+
+  if(repeatDelay)
+    delay = delaySet;
+
+  if(useProgList)
+  {
+    if(progIndex == progEndIdx)
+    {
+      if(loopProgList)
+      {
+        progIndex = 0;
+        nextState = progList[progIndex];
+        progIndex++;
+      }
+      else
+      {
+        useProgList = false;
+        nextState = finProgState;
+      }
+    }
+    else
+    {
+      nextState = progList[progIndex];
+      progIndex++;
+    }
+  }
+
+  if(nextState != NULL)
+  {
+    if(micsFuPtr != NULL)
+      startMics = micsFuPtr();
+
+    nextState();
+
+    if(micsFuPtr != NULL)
+    {
+      diffMics = micsFuPtr() - startMics;
+      curStateRuntime = diffMics;
+
+      if(diffMics > maxStateRuntime[0])
+      {
+        maxStateRuntime[0]  = diffMics;
+        maxRuntimeNumber[0] = curStateNumber;
+      }
+      else if(diffMics > maxStateRuntime[1])
+      {
+        if(curStateNumber != maxRuntimeNumber[0])
+        {
+          maxStateRuntime[1]  = diffMics;
+          maxRuntimeNumber[1] = curStateNumber;
+        }
+      }
+      else if(diffMics > maxStateRuntime[2])
+      {
+        if(curStateNumber != maxRuntimeNumber[1])
+        {
+          maxStateRuntime[2]  = diffMics;
+          maxRuntimeNumber[2] = curStateNumber;
+        }
+      }
+      else if(diffMics > maxStateRuntime[3])
+      {
+        if(curStateNumber != maxRuntimeNumber[2])
+        {
+          maxStateRuntime[3]  = diffMics;
+          maxRuntimeNumber[3] = curStateNumber;
+        }
+      }
+    }
+  }
+  else
+    noStateCounter++;
+
+}
+
+// ---------------------------------------------------------------------------
+// service functions/methods for manipulating the state machine
+// ---------------------------------------------------------------------------
+//
+
+// checking a state for staying (not enter new state)
+//
+bool StateMachine::stayHere()
+{
+  if(repeatState > 0)
+  {
+    repeatState--;
+    if(repeatState == 0)
+    {
+      repeatDelay = false;
+    }
+    return(true);
+  }
+
+  firstEnterToggle = true;
+  return(false);
+}
+
+// setting delay before next state
+//
+void StateMachine::setDelay(int delayTime)
+{
+  delay = (delayTime * frequency) / 1000;
+  repeatDelay = false;
+}
+
+// setting internal speed of the state machine
+// can only be slower than the calling frequency
+//
+void StateMachine::setSpeed(int freq)
+{
+  delaySet      = delay = frequency / freq;
+  repeatDelay   = true;
+}
+
+// setting future state to be called by run
+//
+void StateMachine::enter()
+{
+  if(stayHere()) return;
+
+  pastState = nextState;
+  nextState = futureState;
+}
+
+// setting next state to be called by run
+//
+void StateMachine::enter(StatePtr next)
+{
+  if(stayHere()) return;
+
+  pastState = nextState;
+  nextState = next;
+}
+
+// setting next state with delay (before)
+//
+void StateMachine::enter(StatePtr next, int delayTime)
+{
+  if(stayHere()) return;
+
+  delay = (delayTime * frequency) / 1000;
+  repeatDelay = false;
+
+  pastState = nextState;
+  nextState = next;
+}
+
+// setting next state with repetition
+//
+void StateMachine::enterRep(StatePtr next, int count)
+{
+  if(stayHere()) return;
+
+  repeatState   = count;
+  pastState     = nextState;
+  nextState     = next;
+}
+
+// setting next state with repetition and delay
+//
+void StateMachine::enterRep(StatePtr next, int count, int delayTime)
+{
+  if(stayHere()) return;
+
+  delaySet = delay = (delayTime * frequency) / 1000;
+  repeatDelay = true;
+
+  repeatState   = count;
+  pastState     = nextState;
+  nextState     = next;
+}
+
+// setting state and state after
+//
+void StateMachine::enterVia(StatePtr next, StatePtr then)
+{
+  if(stayHere()) return;
+
+  pastState     = nextState;
+  nextState     = next;
+  futureState   = then;
+}
+
+// calling state
+//
+void StateMachine::call(StatePtr next)
+{
+  if(stayHere()) return;
+
+  pastState     = nextState;
+  futureState   = nextState;
+  nextState     = next;
+}
+
+// setting state and state after with delay
+//
+void StateMachine::enterVia(StatePtr next, StatePtr after, int delayTime)
+{
+  if(stayHere()) return;
+
+  delay = (delayTime * frequency) / 1000;
+  repeatDelay = false;
+
+  pastState     = nextState;
+  nextState     = next;
+  futureState   = after;
+}
+
+// calling state
+//
+void StateMachine::call(StatePtr next, int delayTime)
+{
+  if(stayHere()) return;
+
+  delay = (delayTime * frequency) / 1000;
+  repeatDelay = false;
+
+  pastState     = nextState;
+  futureState   = nextState;
+  nextState     = next;
+}
+
+
+// setting state to state list (program)
+//
+void StateMachine::enterList(StatePtr fin)
+{
+  if(stayHere()) return;
+
+  pastState     = nextState;
+  useProgList   = true;
+  progIndex     = 0;
+  finProgState  = fin;
+}
+
+// setting state to state list (program)and delay
+//
+void StateMachine::enterList(StatePtr fin, int delayTime)
+{
+  if(stayHere()) return;
+
+  delay = (delayTime * frequency) / 1000;
+  repeatDelay = false;
+
+  pastState     = nextState;
+  useProgList   = true;
+  progIndex     = 0;
+  finProgState  = fin;
+}
+
+// setting state to state list (program) at a position
+//
+void StateMachine::enterListAt(StatePtr fin, int index)
+{
+  if(stayHere()) return;
+
+  pastState     = nextState;
+  useProgList   = true;
+  progIndex     = index;
+  finProgState  = fin;
+}
+
+// setting state to state list (program)at a position and delay
+//
+void StateMachine::enterListAt(StatePtr fin, int index, int delayTime)
+{
+  if(stayHere()) return;
+
+  delay = (delayTime * frequency) / 1000;
+  repeatDelay = false;
+
+  pastState     = nextState;
+  useProgList   = true;
+  progIndex     = index;
+  finProgState  = fin;
+}
+
+// detecting the first call of a state
+//
+bool StateMachine::firstEnter()
+{
+  if(!firstEnterToggle)
+    return(false);
+  firstEnterToggle = false;
+  return(true);
+}
+
+// reset first enter detector
+//
+void StateMachine::resetEnter()
+{
+  firstEnterToggle = true;
+}
+
+// counting local cycles
+//
+bool StateMachine::cycle(int cnt)
+{
+  if(callCycleCnt <= 0)
+  {
+    callCycleCnt = cnt;
+    return(true);
+  }
+  callCycleCnt--;
+  return(false);
+}
+//
+bool StateMachine::cycleSec()
+{
+  if(callCycleCnt <= 0)
+  {
+    callCycleCnt = frequency;
+    return(true);
+  }
+  callCycleCnt--;
+  return(false);
+}
+
+// Alternativly returning true and false
+//
+bool StateMachine::toggle()
+{
+  markToggle = !markToggle;
+  return(markToggle);
+}
+
+// Doing only once
+//
+bool StateMachine::oneShot()
+{
+  if(!markOneShot) return(false);
+
+  markOneShot = false;
+  return (true);
+}
+
+void StateMachine::setOneShot()
+{
+  markOneShot = true;
+}
+
+
+// Setable number of returning TRUE
+//
+void  StateMachine::setCondCounter(unsigned int condVal)
+{
+  condCounter = condVal;
+}
+
+bool  StateMachine::condOpen()
+{
+  if(condCounter == 0)
+    return(false);
+  condCounter--;
+  return(true);
+}
+
+// set the time-out value (milliseconds)
+//
+void StateMachine::setTimeOut(int toValue)
+{
+  timeOutCounter = (toValue * frequency) / 1000;
+}
+
+// check the time-out counter
+//
+bool StateMachine::timeOut()
+{
+  if(timeOutCounter > 0)
+    return(false);
+  return(true);
+}
+
+// start time measurement
+//
+void StateMachine::startTimeMeasure()
+{
+  timeMeasureCounter = 0;
+  timeMeasureOn = true;
+}
+
+int StateMachine::getTimeMeasure(bool stop)
+{
+  if(stop)
+    timeMeasureOn = false;
+  return(cycleTime * timeMeasureCounter);
+}
+
+unsigned long StateMachine::getExtTimeMeasure(bool stop)
+{
+  if(stop)
+    timeMeasureOn = false;
+  return(cycleTime * timeMeasureCounter);
+}
+
+// ---------------------------------------------------------------------------
+// Debug and statistic functions/methods
+// ---------------------------------------------------------------------------
+//
+int StateMachine::getDelay()
+{
+  return(delay);
+}
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/StateMachine/StateMachine.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/StateMachine/StateMachine.h
new file mode 100644
index 0000000000000000000000000000000000000000..b5a6ecea89a1a4a81795bf2992e52dd58bb350d2
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/StateMachine/StateMachine.h
@@ -0,0 +1,171 @@
+// ---------------------------------------------------------------------------
+// File:        StateMachine.cpp
+// Editors:     Robert Patzke,
+// Start:       07. February 2018
+// Last change: 07. February 2018
+// URI/URL:     www.mfp-portal.de, homeautomation.x-api.de
+// Licence:     Creative Commons CC-BY-SA
+// ---------------------------------------------------------------------------
+//
+
+#include    "stdlib.h"
+
+
+#ifndef _StateMachine_h
+#define _StateMachine_h
+// ---------------------------------------------------------------------------
+
+#define NrOfSteps       32
+
+#define AUTBREAK(x,y)   { x.enter(y); return; }
+
+// ---------------------------------------------------------------------------
+// class StateMachine
+// ---------------------------------------------------------------------------
+//
+
+class StateMachine
+{
+  // -------------------------------------------------------------------------
+  // class specific data types
+  // -------------------------------------------------------------------------
+  //
+  typedef void (*StatePtr)(void);
+  typedef unsigned long (*MicsecFuPtr)(void);
+
+private:
+  // -------------------------------------------------------------------------
+  // local variables
+  // -------------------------------------------------------------------------
+  //
+  int       delay;              // Setting of delay for next state
+  int       delaySet;           // Setup value for repeated delays
+  bool      repeatDelay;        // If true, every state is delayed (speed set)
+  int       repeatState;        // Controlled repeating a state
+  bool      useVarState;        // breaking the fixed sequence of states by
+                                // using a variable state (in futureState)
+
+  bool        useProgList;            // control progList usage
+  bool        loopProgList;           // looping proglist
+  StatePtr    progList[NrOfSteps];    // dynamically created state run list
+  int         progIndex;              // Program counter (index to next state)
+  int         progEndIdx;             // Index to next empty progList entry
+  StatePtr    finProgState;           // State after using state list
+  MicsecFuPtr micsFuPtr;              // Pointer to micsec function
+
+  bool      firstEnterToggle;       // Is set to true when state changes
+  int       timeOutCounter;         // automatic decremented counter
+
+  bool            timeMeasureOn;          // control of the measurement counter
+  unsigned long   timeMeasureCounter;     // triggered automatic incremented counter
+
+  int       callCycleCnt;           // For cycles inside a state
+  bool      markToggle;             // For bit complements
+  bool      markOneShot;            // for doing only one time
+
+  unsigned int  condCounter;        // Counter for questionable conditions
+
+  // -------------------------------------------------------------------------
+  // local functions/methods
+  // -------------------------------------------------------------------------
+  //
+  bool stayHere();
+
+public:
+  // -------------------------------------------------------------------------
+  // public variables
+  // -------------------------------------------------------------------------
+  //
+  StatePtr  nextState;          // Pointer for indirect calling next state
+  StatePtr  pastState;          // Pointer of the past state
+  StatePtr  futureState;        // Pointer to a future state (see useVarState)
+  StatePtr  doAlways;           // Pointer to a always called state
+  int       cycleTime;          // Cycle time in milliseconds
+  int       frequency;          // Frequency in Hertz (1/s)
+  int       userStatus;         // A number presenting the visible state
+
+  // statistic information
+  //
+  static int   StateMachine_InstCounter;
+
+  unsigned int    noStateCounter;       // Counter for empty running of state machine
+  unsigned int    instNumber;           // Number of this instance
+  unsigned int    runCounter;           // Counter for running states
+  unsigned int    curStateNumber;       // Number of current state
+
+  unsigned long   curStateRuntime;      // run time of latest state
+  unsigned long   maxStateRuntime[4];   // Maximum run time of a state
+  unsigned long   maxRuntimeNumber[4];  // Number of the state of maximum runtime
+
+  // error and debug support
+  //
+  int   userError;
+
+  // -------------------------------------------------------------------------
+  // constructors and initialisations
+  // -------------------------------------------------------------------------
+  //
+  StateMachine();
+  StateMachine(StatePtr firstState, StatePtr anyState, int cycle);
+  void begin(StatePtr firstState, StatePtr anyState, int cycle);
+  StateMachine(StatePtr firstState, StatePtr anyState, int cycle, MicsecFuPtr micsecFu);
+  void begin(StatePtr firstState, StatePtr anyState, int cycle, MicsecFuPtr micsecFu);
+
+  // -------------------------------------------------------------------------
+  // user functions
+  // -------------------------------------------------------------------------
+  //
+  void      run();                                  // has to be cyclic called
+  void      setDelay(int delayTime);                // delay before next state
+  void      setSpeed(int freq);                     // internal run frequency
+
+  void      enter();                                // set next to future state
+  void      enter(StatePtr state);                  // set next state to run
+  void      enter(StatePtr state, int delayTime);   // ... delayed
+
+  void      enterRep(StatePtr state, int count);    // repeat next state
+  void      enterRep(StatePtr state, int count, int delayTime);
+
+  void      call(StatePtr state);                   // set next state and return
+  void      call(StatePtr state, int delayTime);    // ... delayed
+
+  void      enterVia(StatePtr next, StatePtr future);       // next and then
+  void      enterVia(StatePtr next, StatePtr future, int delayTime);
+
+  void      enterList(StatePtr fin);
+  void      enterList(StatePtr fin, int delayTime);
+
+  void      enterListAt(StatePtr fin, int index);
+  void      enterListAt(StatePtr fin, int index, int delayTime);
+
+  bool      firstEnter();       // true only, if the state is first entered
+  void      resetEnter();       // reset first enter mark
+  bool      cycle(int cnt);     // true only, if called <cnt> times
+  bool      cycleSec();         // true only, if a second passed
+  bool      toggle();           // alternating return true and false
+  bool      oneShot();          // only one time true for a call
+  void      setOneShot();       // preparing oneShot to be true
+
+  void      setTimeOut(int toValue);        // Set time-out counter
+  bool      timeOut();                      // Check time-out counter
+  void      startTimeMeasure();             // Start time measurement
+  int       getTimeMeasure(bool stop);      // Time in milliseconds
+
+  unsigned long getExtTimeMeasure(bool stop);     // Time in milliseconds
+
+  void      setCondCounter(unsigned int cntVal);  // Set condition counter
+  bool      condOpen();                           // Ask for open conditions
+
+  // -------------------------------------------------------------------------
+  // debug functions
+  // -------------------------------------------------------------------------
+  //
+  int       getDelay();
+
+};
+
+
+
+
+// ---------------------------------------------------------------------------
+#endif
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/StateMachine/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/StateMachine/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..4dbee447df0f4ef48684a8d78288826ff419108f
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/StateMachine/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "StateMachine",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfBuf.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfBuf.h
new file mode 100644
index 0000000000000000000000000000000000000000..c2f41d77dacb2618fee37685820740222e70b8b3
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfBuf.h
@@ -0,0 +1,48 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   IntrfBuf.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   22. November 2021
+//
+// Eine Schnittstelle zu Puffern (speziell Ringpuffer für Kommunikation)
+//
+
+#ifndef IntrfBuf_h
+#define IntrfBuf_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+
+
+class IntrfBuf
+{
+public:
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+
+
+  // --------------------------------------------------------------------------
+  // Datenzugriffe
+  // --------------------------------------------------------------------------
+  //
+  virtual bool  getByteSnd(byte *dest);   // Byte aus Puffer zum Senden holen
+                                          // Rückgabe FALSE: Puffer leer
+
+  virtual void  putByteRec(byte b);       // Byte vom Empfang an Puffer geben
+
+  virtual int   putSeq(byte *msg, int n); // Bytefolge an Puffer übergeben
+
+};
+
+// ----------------------------------------------------------------------------
+#endif  // IntrfBuf_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfGpio.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfGpio.h
new file mode 100644
index 0000000000000000000000000000000000000000..d37fdf6765d077ac1bc20944080dc257f55e0a8a
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfGpio.h
@@ -0,0 +1,122 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   IntrfGpio.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   04. April 2022
+//
+// Eine Schnittstelle zu den unterschiedlichen Ports in Mikrocontrollern
+//
+
+#ifndef IntrfGpio_h
+#define IntrfGpio_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+
+typedef enum _ifPortNumber
+{
+  ifPort0,
+  ifPort1,
+  ifPort2,
+  ifPort3,
+  ifPort4,
+  ifPort5,
+  ifPort6,
+  ifPort7,
+  ifPort8,
+  ifPort9
+} ifPortNumber;
+
+typedef enum
+{
+  GEnoError,
+  GEcdictPar
+} GpioError;
+
+typedef struct _GpioMask
+{
+  dword       port;
+  dword       pins;
+} GpioMask, *GpioMaskPtr;
+
+typedef struct _GpioExtMask
+{
+  dword         port;
+  dword         pins;
+  _GpioExtMask  *next;
+} GpioExtMask, *GpioExtMaskPtr;
+
+typedef struct _GpioRef
+{
+  dword     *ioPtr;
+  dword     pins;
+} GpioRef, *GpioRefPtr;
+
+typedef struct _GpioExtRef
+{
+  dword       *ioPtr;
+  dword       pins;
+  _GpioExtRef *next;
+} GpioExtRef, *GpioExtRefPtr;
+
+typedef struct _GpioExtVal
+{
+  dword       value;
+  _GpioExtVal *next;
+} GpioExtVal, *GpioExtValPtr;
+
+// Spezifikation der Schnittstellentreiber
+//
+#define IfDrvInput          0x0000
+#define IfDrvOutput         0x0001
+#define IfDrvStrongHigh     0x0002
+#define IfDrvStrongLow      0x0004
+#define IfDrvOpenDrain      0x0008
+#define IfDrvOpenSource     0x0010
+#define IfDrvPullUp         0x0020
+#define IfDrvPullDown       0x0040
+#define IfDrvPullStrong     0x0080
+
+typedef enum
+{
+  ArdD2D5,
+  ArdA0A3,
+  ArdA4A5,
+  ArdA0A5,
+  ArdA4A7,
+  ArdA0A7
+} ArdMask;
+
+class IntrfGpio
+{
+public:
+
+  //virtual ~IntrfGpio();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  virtual GpioError config(int nr, unsigned int cnfBits, GpioExtRefPtr refPtr);
+  virtual GpioError config(int nrFrom, int nrTo, unsigned int cnfBits, GpioExtRefPtr refPtr);
+  virtual GpioError config(GpioExtMask mask, unsigned int cnfBits, GpioExtRefPtr refPtr);
+  virtual GpioError configArd(ArdMask ardMask, unsigned int cnfBits);
+
+  // --------------------------------------------------------------------------
+  // Anwendungsfunktionen
+  // --------------------------------------------------------------------------
+  //
+  virtual void      read(GpioExtRefPtr refPtr, GpioExtValPtr valPtr);
+  virtual dword     readArd(ArdMask ardMask);
+
+  virtual void      write(GpioExtRefPtr refPtr, GpioExtValPtr valPtr);
+  virtual void      writeArd(ArdMask ardMask, dword value);
+  virtual void      set(GpioExtRefPtr refPtr);
+  virtual void      clr(GpioExtRefPtr refPtr);
+};
+
+// ----------------------------------------------------------------------------
+#endif //IntrfGpio_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfRadio.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfRadio.h
new file mode 100644
index 0000000000000000000000000000000000000000..470277ab4f365581507365d1f412a47683463662
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfRadio.h
@@ -0,0 +1,154 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   IntrfRadio.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   9. Mai 2021
+//
+// Eine Schnittstelle zu den unterschiedlichen Transceivern in Mikrocontrollern
+// oder Boards mit Mikrocontrollern und Radio-Transceivern
+//
+
+#ifndef IntrfRadio_h
+#define IntrfRadio_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+#include "bleSpec.h"
+
+typedef struct _TxState
+{
+  unsigned int  prgLoopPrep;
+  unsigned int  evtLoopRampUp;
+  unsigned int  evtLoopTrans;
+  byte  *       txBufferPtr;
+} TxState, *TxStatePtr;
+
+typedef struct  _Channel
+{
+  int   idx;
+  int   freq;
+} Channel, *ChannelPtr;
+
+// Zustand des Datenempfangs (Bit)
+//
+#define RECSTAT_ADDRESS   0x0001
+#define RECSTAT_PAYLOAD   0x0002
+#define RECSTAT_END       0x0004
+#define RECSTAT_DISABLED  0x0008
+#define RECSTAT_CRCOK     0x0010
+
+// Modi für das Senden von Telegrammen
+//
+typedef enum _TxMode
+{
+  txmBase,      // Einzelne Sendung, Endezustand DISABLED
+  txmRepStart,  // Wiederholte Sendung Start, Endezustand END
+  txmRepCont,   // Wiederholte Sendung Fortsetzung, Endezustand END
+  txmRepEnd,    // Wiederholte Sendung Ende, Endezustand DISABLED
+  txmReadPrep,  // Einzelne Sendung mit Empfangsvorbereitung, Endezustand READY
+  txmRead,      // Einzelne Sendung und Empfang, Endezustand END
+  txmPoll,      // Einzelne Sendung und Empfang, Endezustand DISABLED
+  txmReadS,     // Einzelne sendung und Empfang mit Daten, Endezustand END
+  txmRespE,     // Empfang für spezifische Antwort (leeres Polling)
+  txmResp       // Empfang für spezifische Antwort (Datenübertragung)
+} TxMode;
+//
+#define NrOfTxModes   10
+
+// Protokollspezifische Adresseninhalte und Festlegungen
+//
+#define PollPduSize   8
+#define PollAdrSize   6
+
+// len = PduMem[1]
+#define BLE_LEN       pduMem[1]
+// Adr[1] = PduMem[3]
+#define BLE_ADR1      pduMem[3]
+
+#define SOAAP_NAK     0x40
+#define SOAAP_EADR    0x80
+
+// Modeabhängige Statistikdaten
+//
+typedef struct _TxStatistics
+{
+  TxMode      mode;
+  dword       interrupts;
+  dword       recs;
+  dword       sendings;
+  dword       aliens;
+  dword       wrongs;
+  dword       pollAcks;
+  dword       pollNaks;
+  dword       crcErrors;
+  dword       intErrors;
+  byte        memDumpRec[16];
+  byte        memDumpSnd[16];
+} TxStatistics, *TxStatisticsPtr;
+
+class IntrfRadio
+{
+public:
+  // Kanalfrequenzen (Offsets zur Basisfrequenz) und Whitening-Preset
+  // Die ersten 3 Kanäle sind Bewerbungskanäle.
+  // Daran anschließend sind die Kanäle grob nach Frequenz einsortiert.
+  // Diese Liste kann zur Laufzeit an Störungen angepasst werden und wird aufsteigend angewendet
+  //
+  Channel channelList[40] =
+  {
+      {37, 2} , {38,26} , {39,80} , { 1, 6} , { 3,10} , { 5,14} , { 7,18} , { 9,22} ,
+      {12,30} , {14,34} , {16,38} , {18,42} , {20,46} , {22,50} , {24,54} , {26,58} ,
+      {28,62} , {30,66} , {32,70} , {34,74} , {35,76} , { 2, 8} , { 4,12} , { 6,16} ,
+      { 8,20} , {33,72} , {31,68} , {13,32} , {15,36} , {17,40} , {19,44} , {21,48} ,
+      {23,52} , {25,56} , {27,60} , {29,64} , {11,28} , {10,24} , { 0, 4} , {36,78}
+  };
+
+  //virtual ~IntrfRadio();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  virtual void  begin();
+  virtual void  setAccessAddress(dword addr);
+  virtual void  setPacketParms(blePduType type);
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+  virtual void  setChannel(int chnr);         // Schalten physikalischer Kanal
+  virtual int   sendSync(bcPduPtr inPduPtr, TxStatePtr refState);
+
+  virtual void  send(bcPduPtr inPduPtr, TxMode txMode);
+  virtual void  send(bcPduPtr inPduPtrE, bcPduPtr inPduPtrS, TxMode txMode, bool newValues);
+  // Senden (und/oder Empfang) eines Telegramms in einem festgelegten Modus
+
+  virtual void  disable(TxMode txMode);       // Funk AUS für Betriebswechsel
+  virtual bool  disabled(TxMode txMode);      // Abfrage, ob ausgeschaltet
+  virtual void  cont(TxMode txMode);          // aktuellen Vorgang fortsetzen
+  virtual bool  fin(TxMode txMode, bool *err);  // Abfrage ob aktueller Vorgang beendet
+// virtual int   getRecData(bcPduPtr data, TxMode txMode, int max); // Lennard: Deaktiviert
+
+  virtual int   startRec();                   // Datenempfang starten
+  virtual int   contRec();                    // Datenempfang fortsetzen
+  virtual int   endRec();                     // Datenempfang beenden
+  virtual int   checkRec();                   // Zustand Datenempfang feststellen
+  virtual int   getRecData(bcPduPtr data, int max);  // Empfangene Daten lesen
+
+  virtual void  setPower(int DBm);            // Leistung des Senders in DBm
+
+  // --------------------------------------------------------------------------
+  // Datenzugriffe
+  // --------------------------------------------------------------------------
+  //
+  virtual int   getStatistics(TxStatisticsPtr dest);
+  virtual int   getState();                   // Chip-abhängiger Funk-Status
+
+};
+
+// ----------------------------------------------------------------------------
+#endif  // IntrfRadio_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfSerial.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfSerial.h
new file mode 100644
index 0000000000000000000000000000000000000000..909a810a3282ef0e1ee36e79286bd5c4df0bb929
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfSerial.h
@@ -0,0 +1,101 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   IntrfSerial.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   17. November 2021
+//
+// Eine Schnittstelle zu den seriellen Schnittstellen in Mikrocontrollern
+// oder Boards mit Mikrocontrollern und seriellen schnittstellen
+//
+
+#ifndef IntrfSerial_h
+#define IntrfSerial_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+#include "IntrfBuf.h"
+
+typedef enum _SerSpeed
+{
+  Baud1200,
+  Baud2400,
+  Baud4800,
+  Baud9600,
+  Baud14400,
+  Baud19200,
+  Baud28800,
+  Baud31250,
+  Baud38400,
+  Baud56000,
+  Baud57600,
+  Baud76800,
+  Baud115200,
+  Baud230400,
+  Baud250000,
+  Baud460800,
+  Baud921600,
+  Baud1Meg
+} SerSpeed;
+
+typedef enum _SerType
+{
+  stStd,      // Typischer Ausgang ca. 2 mA
+  stPow,      // Starker Ausgang ca. 10 mA
+  stCur       // Open Collector/Drain ca. 10 mA Stromschleife
+} SerType;
+
+typedef struct _SerParams
+{
+  int         inst;           // Nummer (Index) der Ser-Instanz
+  int         txdPort;        // Nummer (Index) des Port fuer TX
+  int         txdPin;         // Nummer (Index) des Pin fuer TX
+  int         rxdPort;        // Nummer (Index) des Port fuer RX
+  int         rxdPin;         // Nummer (Index) des Pin fuer RX
+  SerSpeed    speed;          // Bitrate (Baud)
+  SerType     type;           // Ausgang
+} SerParams, *SerParamsPtr;
+
+typedef enum _SerError
+{
+  SEnoError
+} SerError;
+
+
+class IntrfSerial
+{
+public:
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  virtual void  begin(SerParamsPtr serParPtr, IntrfBuf * bufferIf);
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+  virtual void resuSend();    // Fortsetzen des Interrupt-Sendebetriebs
+  virtual void startSend();   // Starten des Sendebetriebs
+  virtual void stopSend();    // Anhalten des Sendebetriebs
+
+  virtual void startRec();    // Starten des Empfangsbetriebs
+  virtual void stopRec();     // Anhalten des Empfangsbetriebs
+
+
+  // --------------------------------------------------------------------------
+  // Datenzugriffe
+  // --------------------------------------------------------------------------
+  //
+  virtual bool condSend(byte c);  // Bedingtes Senden eines Zeichens
+
+  virtual int   getLastError();   // Letzten Fehler lesen (Bits)
+  virtual int   getAnyError();    // Alle vorgekommenen Fehlerbits
+  virtual dword getErrCount();    // Anzahl der Fehler lesen
+
+};
+
+// ----------------------------------------------------------------------------
+#endif  // IntrfRadio_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfTimer.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfTimer.h
new file mode 100644
index 0000000000000000000000000000000000000000..2777f5ba7b80b1116baa9030c452037dc4507952
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfTimer.h
@@ -0,0 +1,55 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   IntrfTimer.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   29. Juni 2021
+//
+// Eine Schnittstelle zu den unterschiedlichen Timern in Mikrocontrollern
+//
+
+#ifndef IntrfTimer_h
+#define IntrfTimer_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+
+typedef enum _ifTimerNumber
+{
+  ifTimer0,
+  ifTimer1,
+  ifTimer2,
+  ifTimer3,
+  ifTimer4,
+  ifTimer5,
+  ifTimer6,
+  ifTimer7,
+  ifTimer8,
+  ifTimer9
+} ifTimerNumber;
+
+class IntrfTimer
+{
+public:
+
+  //virtual ~IntrfTimer();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  virtual void  setMilli(ifTimerNumber timNr, int milliSec, int repeat);
+  //virtual void  setMilli(ifTimerNumber timNr, int milliSec, int repeat, dword ISR);
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+  virtual bool  milli();        // Abfrage des Timer, <true> wenn abgelaufen
+
+};
+
+// ----------------------------------------------------------------------------
+#endif //IntrfTimer_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfTw.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfTw.h
new file mode 100644
index 0000000000000000000000000000000000000000..f0d97b622e98f8f48ee8f066912caf1b4723bc89
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/IntrfTw.h
@@ -0,0 +1,125 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   IntrfTw.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   3. Juni 2021
+//
+// Eine Schnittstelle zu den unterschiedlichen I2C Schnittstellen
+// oder Boards mit geeigneten Mikrocontrollern
+//
+
+#ifndef IntrfTw_h
+#define IntrfTw_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+
+typedef enum _TwiDevType
+{
+  TwiMaster,
+  TwiSlave
+} TwiDevType;
+
+typedef enum _TwiSpeed
+{
+  Twi100k,      // Standard, sollte unterstützt werden
+  Twi250k,
+  Twi400k       // Schnell, sollte unterstützt werden
+} TwiSpeed;
+
+typedef struct _TwiParams
+{
+  int         inst;           // Nummer (Index) der Twi-Instanz
+  TwiDevType  type;           // Auswahl Master/Slave
+  int         clkPort;        // Nummer (Index) des Port fuer Takt
+  int         clkPin;         // Nummer (Index) des Pin fuer Takt
+  int         dataPort;       // Nummer (Index) des Port fuer Daten
+  int         dataPin;        // Nummer (Index) des Pin fuer Daten
+  TwiSpeed    speed;
+} TwiParams, *TwiParamsPtr;
+
+typedef enum _TwiError
+{
+  TEnoError
+} TwiError;
+
+typedef enum _TwiStatus
+{
+  TwStUnborn,
+  TwStRdReq,
+  TwStWrReq,
+  TwStSent,
+  TwStRecvd,
+  TwStFin     = 0x0080,
+  TwStError   = 0x0100,
+  TwStOverrun = 0x0101,
+  TwStAdrNak  = 0x0102,
+  TwStDataNak = 0x0104
+} TwiStatus;
+
+typedef struct _TwiByte
+{
+  TwiStatus   twiStatus;
+  byte        value;
+} TwiByte, *TwiBytePtr;
+
+typedef struct _TwiWord
+{
+  TwiStatus   twiStatus;
+  word        value;
+} TwiWord, *TwiWordPtr;
+
+typedef struct _TwiByteSeq
+{
+  TwiStatus   twiStatus;
+  int         len;
+  byte        *valueRef;
+} TwiByteSeq, *TwiByteSeqPtr;
+
+
+class IntrfTw
+{
+public:
+  // --------------------------------------------------------------------------
+  // Konstruktoren und Initialisierungen
+  // --------------------------------------------------------------------------
+  //
+  virtual TwiError begin(TwiParamsPtr inParPtr);
+
+  //virtual ~IntrfTw();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  virtual void getParams(TwiParamsPtr parPtr);
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+
+  // --------------------------------------------------------------------------
+  // Datenaustausch
+  // --------------------------------------------------------------------------
+  //
+  // asynchrone Kommunikation, Zustand in TwiByte.twiStatus
+  //
+  virtual TwiError sendByte(int adr, TwiBytePtr refByte);
+  virtual TwiError sendByteReg(int adr, int reg, TwiBytePtr refByte);
+  virtual TwiError recByteReg(int adr, int reg, TwiBytePtr refByte);
+  virtual TwiError recByteRegSeq(int adr, int reg, TwiByteSeqPtr refByteSeq);
+
+  // synchrone Kommunikation
+  //
+  virtual TwiStatus writeByteReg(int adr, int reg, byte value);
+  virtual int       readByteReg(int adr, int reg);
+  virtual TwiStatus readByteRegSeq(int adr, int reg, TwiByteSeqPtr refByteSeq);
+
+};
+
+// ----------------------------------------------------------------------------
+#endif  // IntrfTw_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/SoaapBleMaster.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/SoaapBleMaster.h
new file mode 100644
index 0000000000000000000000000000000000000000..98bd22eb99c0839cf9f5ad554dd116b21c0afd81
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/SoaapBleMaster.h
@@ -0,0 +1,63 @@
+// ----------------------------------------------------------------------------
+//                              SoaapBleMaster.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:   1. November 2021
+// Letzte Bearbeitung: 15. März 2022
+//
+
+#ifndef SoaapBleMaster_h
+#define SoaapBleMaster_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
+
+//#define TEST001
+// Ausgaben an serielle schnittstelle zur Prüfung der ap-Zustandsmaschine
+
+#include  "LoopCheck.h"
+#include  "StateMachine.h"
+#include  "nRF52840Radio.h"
+#include  "BlePoll.h"
+#include  "ComRingBuf.h"
+#include  "nRF52840Ser.h"
+#include  "SoaapMsg.h"
+#include  "Monitor.h"
+
+// ----------------------------------------------------------------------------
+// Vorwärtsreferenzen
+// ----------------------------------------------------------------------------
+//
+void ctrlIdle();
+void ctrlInit();
+void sendCtrl();
+void checkCtrl();
+
+void apInit();
+void apWaitDE();
+void apWaitMeas();
+void apProcMeas();
+
+
+#ifdef DebugTerminal
+// ----------------------
+void smInit() ;
+void smCheckJobs() ;
+void smDebDword() ;
+void smCtrlPolling() ;
+void smWaitPolling() ;
+void smReadPollValues() ;
+void smCheckSer();
+// ----------------------
+#endif
+
+#endif /* SoaapBleMaster_h */
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/SoaapBleSlave.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/SoaapBleSlave.h
new file mode 100644
index 0000000000000000000000000000000000000000..f3ae7f7500126e35f19524b4188a3044d43dfaf4
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/SoaapBleSlave.h
@@ -0,0 +1,43 @@
+#ifndef SoaapBleSlave_h
+#define SoaapBleSlave_h
+
+#include  "LoopCheck.h"
+#include  "StateMachine.h"
+#include  "nRF52840Radio.h"
+#include  "BlePoll.h"
+#include  "ComRingBuf.h"
+#include  "nRF52840Ser.h"
+#include  "SoaapMsg.h"
+#include  "Monitor.h"
+#include "nRF52840Twi.h"
+#include "SensorLSM9DS1.h"
+#include "nRF52840Gpio.h"
+
+//#define SlaveACM1
+bool sendData(PlpType plpType, byte *dest);
+bool getValues(PlpType plpType, byte *dest);
+void smInit();
+void smCheckJobs();
+void smCheckSens();
+
+void smDebDword();
+void smCtrlPolling();
+void smWaitPolling();
+void smSensReset1();
+void smSensReset2();
+
+void smSensGetValues1();
+void smSensGetValues2();
+void smSensGetValues3();
+void smSensGetValues4();
+void smSensGetValues5();
+void smSensGetValues6();
+void smSensGetValues7();
+
+void smSensGetSoaapValues();
+void smSensDebugValues();
+
+void smSensGetErrors();
+void smSensGetTimeOuts();
+void smSensGetRunCounts();
+#endif
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/arduinoDefs.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/arduinoDefs.h
new file mode 100644
index 0000000000000000000000000000000000000000..4830c5d860683336800903c177b10c53fcb73736
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/arduinoDefs.h
@@ -0,0 +1,53 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Definitions instead Arduino
+// Datei:   arduinoDefs.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+// These definitions shall encapsulate the Arduino environment
+// The file should be included instead of Arduino.h for arbitrary environments
+//
+
+#ifndef _arduinoDefs_h
+#define _arduinoDefs_h
+
+// ---------------------------------------------------------------------------
+// Simple type definitions and settings
+// ---------------------------------------------------------------------------
+//
+#undef  byte
+#undef  word
+#undef  dword
+
+#define byte unsigned char
+#define word unsigned short
+#define dword unsigned long
+
+#ifdef smnLinGcc64
+
+#undef  byte
+#undef  word
+#undef  dword
+
+#define byte unsigned char
+#define word unsigned short
+#define dword unsigned int
+
+#endif
+
+// ---------------------------------------------------------------------------
+// Complex type definitions
+// ---------------------------------------------------------------------------
+// The following types are more complex definitions with Arduino.
+// But here simple types are used if possible, because we only care for the
+// environment that is needed by Social Manufacturing Network
+//
+
+#ifndef IPAddress
+  #define IPAddress   byte *
+#endif
+
+
+#endif  // _arduinoDefs_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/bleSpec.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/bleSpec.h
new file mode 100644
index 0000000000000000000000000000000000000000..a4993a39fee05fa5162fc888acf0804e5fc59254
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/bleSpec.h
@@ -0,0 +1,86 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   bleSpec.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   8. Mai 2021
+//
+// Der Inhalt dieser Datei ist der Blootooth Spezifikation 5.2 entnommen
+// bzw. daran angelehnt.
+// Insbesondere dem Teil 6, Low Energy Controller
+//
+
+#ifndef bleSpec_h
+#define bleSpec_h
+// ----------------------------------------------------------------------------
+
+#ifndef octet
+typedef unsigned char octet;
+#endif
+
+#define VersionBTS  52
+#define TypeBTS     BLE
+
+// ----------------------------------------------------------------------------
+// Festlegungen für die Bewerbungskanäle (Advertizing Physical Channels)
+// ----------------------------------------------------------------------------
+
+// Basisfrequenz in MHz
+#define BaseFrequency   2400
+
+// Zugriffsadresse (Access Address)
+#define AdvAccAddr  0x8E89BED6
+
+// CRC-Polynom
+#define PolynomCRC  0x0100065B
+
+// CRC-Startwert
+#define AdvStartCRC 0x555555
+
+// Geräteadresse
+typedef octet  BD_ADR[6];
+
+// Telegrammtypen (PDU Types)
+#define ADV_IND           0x0
+#define ADV_DIRECT_IND    0x1
+#define ADV_NONCONN_IND   0x2
+#define SCAN_REQ          0x3
+#define SCAN_RSP          0x4
+#define CONNECT_IND       0x5
+#define ADV_SCAN_IND      0x6
+
+// Kennzeichnung Art der Geräteadresse (Device Address Mark, TxAdd = 1, random)
+#define DevAdrType  0x2
+
+// Telegrammkopf ohne Längenbyte
+#define HeadS0B     ((ADV_NONCONN_IND << 4) | DevAdrType)
+#define HeadS0BS    ((ADV_SCAN_IND << 4) | DevAdrType)
+
+// Datenstruktur für das zu sendende Telegramm
+// bei der Bewerbung (Advertising)
+//
+typedef struct _bcPdu         // Beacon - PDU
+{
+  byte  head;       // Header = PDU-Typ und Adresskennung (S0 bei nRF52840)
+  byte  len;        // Länge des Telegramms inkl. Adresse (LENGTH bei nRF52840)
+  byte  adr0;       // niedrigstwertiges Adressbyte (S1 bei nRF52840)
+  byte  adr1;       //
+  byte  adr2;       //      Das ist die Geräteadresse, die hier wahlfrei ist
+  byte  adr3;       //      Sie wird zur Identifikation des Gerätes verwendet
+  byte  adr4;       //
+  byte  adr5;       // höchstwertiges Addressbyte
+  byte  data[31];   // Nutzdaten (maximale Anzahl nach BLE-Spez.)
+} bcPdu, *bcPduPtr;
+
+// Telegrammtypen
+//
+typedef enum  _blePduType
+{
+  bptAdv,           // Standard-Bewerbungstelegramm
+  bptAux
+} blePduType;
+
+// ----------------------------------------------------------------------------
+#endif  // bleSpec_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/environment.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/environment.h
new file mode 100644
index 0000000000000000000000000000000000000000..311c3331bd39a97884ba12b85375b08a418813d0
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/environment.h
@@ -0,0 +1,126 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   environment.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#ifndef _environment_h
+#define _environment_h
+
+#ifdef  smnDEFBYBUILD
+
+// Alternative Festlegungen durch Settings in der Build-Umgebung der IDE
+// Das erspart die Verwaltung mehrerer/unterschiedlicher environment.h Dateien
+// in den Projekten
+//
+  #ifdef smnUBUNTU_ECLIPSE
+  // Meine Entwicklungs- und Testumgebung auf dem Notebook unter UBUNTU
+  // für Sketche, die auf dem PC laufen (u.a. zum Debuggen)
+
+    #define smnSimLinux
+    #define smnNotebook
+    #define smnLINUX
+    #define smnDebugLinuxConsole
+
+  #endif
+
+  #ifdef smnWIN32_VS
+  // Meine Entwicklungs- und Testumgebung auf dem Notebook unter Windows
+  // für Sketche, die auf dem PC laufen (u.a. zum Debuggen)
+
+    #define smnSimWindows
+    #define smnNotebook
+    #define smnWIN32
+    #define smnDebugWindowsConsole
+
+  #endif
+
+  #ifdef smnSLOELIN
+  // Mit Sloeber auf Ubuntu/Linux für Entwicklungen zum ESP32
+  //
+
+    #define smnDebugArduino
+    #define smnSloeber
+    #define smnESP32_DEV
+    #define smnESP32
+    #define smnSerial Serial
+  #endif
+
+  #ifdef smnSLOEDUE
+  // Mit Sloeber auf Ubuntu/Linux für Entwicklungen zum Arduino Due
+  //
+
+    #define smnDebugArduino
+    #define smnSloeber
+    #define smnArduinoDUE
+    #define smnArduinoShieldEth
+    #define smnSAM3X
+    #define smnSerial Serial
+  #endif
+
+  #ifdef smnNANOBLE33
+    #define smnDebugArduino
+    #define smnSloeber
+    #define smnArduinoNanoBLE33
+    #define smnNRF52840
+    #define smnSerial Serial
+  #endif
+
+  #ifdef smnSLOESAMD21
+    #define smnDebugArduino
+    #define smnSloeber
+    #define smnArduinoZeroSamD21
+    #define smnSAMD21G18
+    #define smnSD21MINI
+    #define smnSerial SerialUSB
+  #endif
+
+#else
+
+// Die Definitionen werden hier in den meisten Fällen grundsetzlich ausgewertet,
+// nicht über spezifische Werte.
+// Die Abfrage geschieht also mit #ifdef
+// Daraus folgt hier eine Liste der Möglichkeiten, die beliebig erweitert werden kann.
+// Die Auswahl erfolgt schließlich über aus- bzw. einkommentieren.
+//
+
+// Übergeordnete Festlegungen
+// ---------------------------------------------------------------------------
+//
+#define smnDebugArduino
+//#define smnDebugLinuxConsole
+//
+
+// IDE, Editor, etc.
+// ---------------------------------------------------------------------------
+//
+//#define smnSimLinux
+#define smnSloeber
+//#define smnArduinoIDE
+//#define smnPlatformIO
+
+// Hardware, Boards, etc.
+// ---------------------------------------------------------------------------
+//
+//#define smnNotebook
+//#define smnESP32_DEV
+#define smnNodeMCU10
+//#define smnArduinoDUE
+//#define smnArduinoShieldEth
+//#define smnArduinoShieldWiFi
+#define smnSerial Serial
+
+// Prozessoren, Microcontroller, Betriebssysteme, etc.
+// ---------------------------------------------------------------------------
+//
+//#define smnLINUX
+//#define smnESP32
+#define smnESP8266
+//#define smnSAM3X
+
+#endif  // smnDEFBYBUILD
+
+#endif  // _environment_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..9e3641f6fb0e38c5e74b3c3cee96651efbb2fdaa
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "environment",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/socManNetUser.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/socManNetUser.h
new file mode 100644
index 0000000000000000000000000000000000000000..4809a3b98de3da0b84f1f25ba7cda8af91bb2672
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/environment/socManNetUser.h
@@ -0,0 +1,315 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Broadcast Socket Interface
+// Datei:   socManNetUser.h
+// Editor:  Igor Farber, Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#ifndef _socManNetUser_h
+#define _socManNetUser_h
+
+#define smnMULTIBOARDS
+
+#define smnBOARD001
+//#define smnBOARD002
+//#define smnBOARD003
+//#define smnBOARD004
+//#define smnBOARD005
+//#define smnBOARD006
+
+
+
+#ifndef smnMyNet
+// ***************************************************************************
+// define smnMyNet in project properties (Arduino) with -DsmnMyNet
+// to use your own network definition file included at the end of this file
+
+
+//---------------------------------------------------------------------------//
+//-------------------------------------------------------------------------
+// MAC-Adresse des Ethernet-Shield
+//-------------------------------------------------------------------------
+#ifdef  smnMULTIBOARDS
+
+  #ifdef  smnBOARD001
+    #define LOCAL_MAC_ADR_B0 0x90
+    #define LOCAL_MAC_ADR_B1 0xa2
+    #define LOCAL_MAC_ADR_B2 0xda
+    #define LOCAL_MAC_ADR_B3 0x0f
+    #define LOCAL_MAC_ADR_B4 0x1b
+    #define LOCAL_MAC_ADR_B5 0x84
+    // MAC-Adresse
+
+    #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-1b-84"
+    // MAC-Adresse als String
+  #endif
+
+  #ifdef  smnBOARD002
+    #define LOCAL_MAC_ADR_B0 0x90
+    #define LOCAL_MAC_ADR_B1 0xa2
+    #define LOCAL_MAC_ADR_B2 0xda
+    #define LOCAL_MAC_ADR_B3 0x0f
+    #define LOCAL_MAC_ADR_B4 0x63
+    #define LOCAL_MAC_ADR_B5 0xb0
+    // MAC-Adresse
+
+    #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-63-b0"
+    // MAC-Adresse als String
+  #endif
+
+  #ifdef  smnBOARD003
+    #define LOCAL_MAC_ADR_B0 0x90
+    #define LOCAL_MAC_ADR_B1 0xa2
+    #define LOCAL_MAC_ADR_B2 0xda
+    #define LOCAL_MAC_ADR_B3 0x0f
+    #define LOCAL_MAC_ADR_B4 0x5b
+    #define LOCAL_MAC_ADR_B5 0xe1
+    // MAC-Adresse
+
+    #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-5b-e1"
+    // MAC-Adresse als String
+  #endif
+
+  #ifdef  smnBOARD004
+    #define LOCAL_MAC_ADR_B0 0x90
+    #define LOCAL_MAC_ADR_B1 0xa2
+    #define LOCAL_MAC_ADR_B2 0xda
+    #define LOCAL_MAC_ADR_B3 0x0f
+    #define LOCAL_MAC_ADR_B4 0x5b
+    #define LOCAL_MAC_ADR_B5 0x8f
+    // MAC-Adresse
+
+    #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-62-d0"
+    // MAC-Adresse als String
+  #endif
+
+  #ifdef  smnBOARD005
+    #define LOCAL_MAC_ADR_B0 0x90
+    #define LOCAL_MAC_ADR_B1 0xa2
+    #define LOCAL_MAC_ADR_B2 0xda
+    #define LOCAL_MAC_ADR_B3 0x0f
+    #define LOCAL_MAC_ADR_B4 0x62
+    #define LOCAL_MAC_ADR_B5 0xd0
+    // MAC-Adresse
+
+    #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-62-d0"
+    // MAC-Adresse als String
+  #endif
+
+  #ifdef  smnBOARD006
+    #define LOCAL_MAC_ADR_B0 0x90
+    #define LOCAL_MAC_ADR_B1 0xa2
+    #define LOCAL_MAC_ADR_B2 0xda
+    #define LOCAL_MAC_ADR_B3 0x0f
+    #define LOCAL_MAC_ADR_B4 0x62
+    #define LOCAL_MAC_ADR_B5 0xd0
+    // MAC-Adresse
+
+    #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-62-d0"
+    // MAC-Adresse als String
+  #endif
+
+#else
+  #define LOCAL_MAC_ADR_B0 0x90
+  #define LOCAL_MAC_ADR_B1 0xa2
+  #define LOCAL_MAC_ADR_B2 0xda
+  #define LOCAL_MAC_ADR_B3 0x0f
+  #define LOCAL_MAC_ADR_B4 0x62
+  #define LOCAL_MAC_ADR_B5 0xd0
+  // MAC-Adresse
+
+  #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-62-d0"
+  // MAC-Adresse als String
+#endif
+
+#define LOCAL_PORT      4100
+// Portnummer lokal
+
+#define BROADCAST_PORT  4100
+// Portnummer Rundruf
+
+#define CONFIG_PORT     4001
+
+//-------------------------------------------------------------------------
+// Sub-Netz-Maske
+//-------------------------------------------------------------------------
+#define SUB_NET_MASK_B0 255
+#define SUB_NET_MASK_B1 255
+#define SUB_NET_MASK_B2 255
+#define SUB_NET_MASK_B3 0
+
+//----------------------------------------------------------------------------
+// IP-Adresse, ist auf das verwendete Netzwerk anzupassen (kein DHCP)
+//----------------------------------------------------------------------------
+
+// Das Netzwerk an sich
+#define LOCAL_IP_B0 192
+#define LOCAL_IP_B1 168
+#define LOCAL_IP_B2 99
+
+#define BROADCAST_IP_B0 192
+#define BROADCAST_IP_B1 168
+#define BROADCAST_IP_B2 99
+#define BROADCAST_IP_B3 255
+
+#ifdef smnMULTIBOARDS
+
+  #ifdef smnBOARD001
+
+    // IP-Lokal
+    // ----------------------------------------------
+    //
+    #define LOCAL_IP_B3 201
+    #define LOCAL_IP_ADR_STR (char *) "192.168.99.201"
+
+    // IP-Broadcast
+    // ----------------------------------------------
+    //
+    #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+  #endif
+
+
+  #ifdef smnBOARD002
+
+    // IP-Lokal
+    // ----------------------------------------------
+    //
+    #define LOCAL_IP_B3 202
+    #define LOCAL_IP_ADR_STR (char *) "192.168.99.202"
+
+    // IP-Broadcast
+    // ----------------------------------------------
+    //
+    #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+  #endif
+
+
+  #ifdef smnBOARD003
+
+    // IP-Lokal
+    // ----------------------------------------------
+    //
+    #define LOCAL_IP_B3 203
+    #define LOCAL_IP_ADR_STR (char *) "192.168.99.203"
+
+    // IP-Broadcast
+    // ----------------------------------------------
+    //
+    #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+  #endif
+
+
+  #ifdef smnBOARD004
+
+    // IP-Lokal
+    // ----------------------------------------------
+    //
+    #define LOCAL_IP_B3 204
+    #define LOCAL_IP_ADR_STR (char *) "192.168.99.204"
+
+    // IP-Broadcast
+    // ----------------------------------------------
+    //
+    #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+  #endif
+
+
+  #ifdef smnBOARD005
+
+    // IP-Lokal
+    // ----------------------------------------------
+    //
+    #define LOCAL_IP_B3 205
+    #define LOCAL_IP_ADR_STR (char *) "192.168.99.205"
+
+    // IP-Broadcast
+    // ----------------------------------------------
+    //
+    #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+  #endif
+
+
+  #ifdef smnBOARD006
+
+    // IP-Lokal
+    // ----------------------------------------------
+    //
+    #define LOCAL_IP_B3 206
+    #define LOCAL_IP_ADR_STR (char *) "192.168.99.205"
+
+    // IP-Broadcast
+    // ----------------------------------------------
+    //
+    #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+  #endif
+
+
+#else
+
+  // IP-Lokal
+  // ----------------------------------------------
+  //
+  #define LOCAL_IP_B3 205
+  #define LOCAL_IP_ADR_STR (char *) "192.168.99.205"
+
+  // IP-Broadcast
+  // ----------------------------------------------
+  //
+  #define BROADCAST_IP_B3 255
+  #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+#endif
+
+// IP-Gateway
+// ----------------------------------------------
+//
+#define GATEWAY_IP_B0 192
+#define GATEWAY_IP_B1 168
+#define GATEWAY_IP_B2 99
+#define GATEWAY_IP_B3 1
+
+
+/******************************************************************************
+ * Netzwerkname
+ * Passwort
+ */
+#define SMNSSID "MPZ-Labor"    //Netzwerk Name
+#define SMNPASS "MPZMPZMPZ"    //Netzwerk Passwort
+
+// IP-primaryDNS
+// ----------------------------------------------
+//
+#define PRIMDNS_IP_B0   8
+#define PRIMDNS_IP_B1   8
+#define PRIMDNS_IP_B2   8
+#define PRIMDNS_IP_B3   8
+
+// IP-secondaryDNS
+// ----------------------------------------------
+//
+#define SECDNS_IP_B0    8
+#define SECDNS_IP_B1    8
+#define SECDNS_IP_B2    4
+#define SECDNS_IP_B3    4
+
+#else
+// ***************************************************************************
+  #undef _socManNetUser_h
+  #include MyNetFile
+  // Define MyNetFile in Project-Properties with -DMyNetFile=\"xxxxx\"
+  // and xxxxx being your filename.
+  // Define also smnMyNet with -DsmnMyNet to enter this include directive
+
+#endif // smnMyNet
+// ***************************************************************************
+
+//---------------------------------------------------------------------------//
+#endif // _socManNetUser_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Gpio/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Gpio/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..a099c0c6f9c5ede5584dbf9ab24da0199b348548
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Gpio/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "nRF52840Gpio",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Gpio/nRF52840Gpio.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Gpio/nRF52840Gpio.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..47a25ddebcff11a2d9ef003cf83ddf1be250d057
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Gpio/nRF52840Gpio.cpp
@@ -0,0 +1,353 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Gpio.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   29. Juni 2021
+//
+
+#include "nRF52840Gpio.h"
+
+  // --------------------------------------------------------------------------
+  // Konstruktoren
+  // --------------------------------------------------------------------------
+  //
+  nRF52840Gpio::nRF52840Gpio()
+  {
+    gpioPtr = NULL;
+  }
+
+
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+
+dword     nRF52840Gpio::getCnfValue(unsigned int cnfBits)
+{
+  dword tmpMask = 0;
+
+  if(cnfBits & IfDrvPullUp) tmpMask |= GpioPinCnf_PULL(GpioPullUp);
+  if(cnfBits & IfDrvPullDown) tmpMask |= GpioPinCnf_PULL(GpioPullDown);
+
+  if((cnfBits & IfDrvOutput))     // Ausgang **********************************
+  {
+    tmpMask |= GpioPinCnf_DIR;
+
+    if(cnfBits & IfDrvStrongHigh)       // StrongHigh = H1
+    {
+      if(cnfBits & IfDrvOpenSource)     // OpenSource = D0
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveD0H1);
+      else if(cnfBits & IfDrvStrongLow) // StrongLow = H0
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveH0H1);
+      else
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveS0H1);
+    }
+    else if(cnfBits & IfDrvStrongLow)   // StrongLow = H0
+    {
+      if(cnfBits & IfDrvOpenDrain)      // OpenDrain = D1
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveH0D1);
+      else
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveH0S1);
+    }
+    else
+    {
+      if(cnfBits & IfDrvOpenSource)     // OpenSource = D0
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveD0S1);
+      else if(cnfBits & IfDrvOpenDrain) // OpenDrain = D1
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveS0D1);
+      else
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveS0S1);
+    }
+  }
+  else                            // Eingang **********************************
+  {
+    tmpMask &= 0xFFFFFFFC;
+  }
+
+  return(tmpMask);
+}
+
+GpioError nRF52840Gpio::config(int nrFrom, int nrTo, unsigned int cnfBits, GpioExtRefPtr refPtr)
+{
+  GpioError retv = GEnoError;
+  int       portNum;
+  int       pinNum;
+  dword     tmpMask;
+  dword     cnfValue;
+
+  tmpMask = IfDrvOpenDrain | IfDrvOpenSource;
+  if((cnfBits & tmpMask) == tmpMask) return (GEcdictPar);
+
+  tmpMask = IfDrvPullDown | IfDrvPullUp;
+  if((cnfBits & tmpMask) == tmpMask) return (GEcdictPar);
+
+  cnfValue  = getCnfValue(cnfBits);
+  tmpMask   = 0;
+
+  // Bedienen des angegebenen Bereiches
+  //
+  for(int i = nrFrom; i <= nrTo; i++)
+  {
+    portNum = (i & 0x0E0) >> 5;
+    pinNum  =  i & 0x01F;
+
+    tmpMask |= (1 << i);
+
+    if(portNum == 0)
+      gpioPtr = NrfGpioPtr0;
+    else
+      gpioPtr = NrfGpioPtr1;
+
+      gpioPtr->PIN_CNF[pinNum] = cnfValue;
+  }
+
+  refPtr->ioPtr = (dword *) gpioPtr;
+  refPtr->pins  = tmpMask;
+
+  return(retv);
+}
+
+GpioError nRF52840Gpio::config(int nr, unsigned int cnfBits, GpioExtRefPtr refPtr)
+{
+  return(config(nr,nr,cnfBits, refPtr));
+}
+
+GpioError nRF52840Gpio::config(GpioExtMask mask, unsigned int cnfBits, GpioExtRefPtr refPtr)
+{
+  GpioError retv = GEnoError;
+  dword     cnfVal;
+
+  cnfVal = IfDrvOpenDrain | IfDrvOpenSource;
+  if((cnfBits & cnfVal) == cnfVal) return (GEcdictPar);
+
+  cnfVal = IfDrvPullDown | IfDrvPullUp;
+  if((cnfBits & cnfVal) == cnfVal) return (GEcdictPar);
+
+  cnfVal = getCnfValue(cnfBits);
+
+  // Bedienen des angegebenen Bereiches
+  //
+  dword chkMask = 1;
+
+  for(int i = 0; i < 32; i++)
+  {
+    if(mask.port == 0)
+      gpioPtr = NrfGpioPtr0;
+    else
+      gpioPtr = NrfGpioPtr1;
+
+    if(mask.pins & chkMask)
+      gpioPtr->PIN_CNF[i] = cnfVal;
+
+    chkMask <<= 1;
+  }
+
+  if(refPtr != NULL)
+  {
+    refPtr->ioPtr = (dword *) gpioPtr;
+    refPtr->pins  = mask.pins;
+  }
+
+  return(retv);
+}
+
+GpioError nRF52840Gpio::configArd(ArdMask ardMask, unsigned int cnfBits)
+{
+  GpioExtMask  ioMask;
+
+  switch(ardMask)
+  {
+    case ArdA0A3:
+      ioMask.port = 0;
+      ioMask.pins = ArdA0Mask | ArdA1Mask | ArdA2Mask | ArdA3Mask;
+      break;
+
+    case ArdA4A7:
+      ioMask.port = 0;
+      ioMask.pins = ArdA4Mask | ArdA5Mask | ArdA6Mask | ArdA7Mask;
+      break;
+
+    case ArdA0A7:
+      ioMask.port = 0;
+      ioMask.pins = ArdA0Mask | ArdA1Mask | ArdA2Mask | ArdA3Mask |
+                    ArdA4Mask | ArdA5Mask | ArdA6Mask | ArdA7Mask;
+      break;
+
+    case ArdD2D5:
+      ioMask.port = 1;
+      ioMask.pins = ArdD2Mask | ArdD3Mask | ArdD4Mask | ArdD5Mask;
+      break;
+  }
+
+  return config(ioMask, cnfBits, NULL);
+}
+
+
+
+  // --------------------------------------------------------------------------
+  // Anwendungsfunktionen
+  // --------------------------------------------------------------------------
+  //
+void      nRF52840Gpio::read(GpioExtRefPtr ioRefPtr, GpioExtValPtr valPtr)
+{
+  gpioPtr = (nrfGpioPtr) ioRefPtr->ioPtr;
+  valPtr->value = gpioPtr->IN;
+}
+
+dword     nRF52840Gpio::readArd(ArdMask ardMask)
+{
+  dword   inVal;
+  dword   retVal;
+
+  retVal = 0;
+
+  switch(ardMask)
+  {
+    case ArdA0A3:
+      inVal = NrfGpioPtr0->IN;
+      if(inVal & ArdA0Mask) retVal |= 0x01;
+      if(inVal & ArdA1Mask) retVal |= 0x02;
+      if(inVal & ArdA2Mask) retVal |= 0x04;
+      if(inVal & ArdA3Mask) retVal |= 0x08;
+      break;
+
+    case ArdA4A7:
+      inVal = NrfGpioPtr0->IN;
+      if(inVal & ArdA4Mask) retVal |= 0x01;
+      if(inVal & ArdA5Mask) retVal |= 0x02;
+      if(inVal & ArdA6Mask) retVal |= 0x04;
+      if(inVal & ArdA7Mask) retVal |= 0x08;
+      break;
+
+    case ArdA0A7:
+      inVal = NrfGpioPtr0->IN;
+      if(inVal & ArdA0Mask) retVal |= 0x01;
+      if(inVal & ArdA1Mask) retVal |= 0x02;
+      if(inVal & ArdA2Mask) retVal |= 0x04;
+      if(inVal & ArdA3Mask) retVal |= 0x08;
+      if(inVal & ArdA4Mask) retVal |= 0x10;
+      if(inVal & ArdA5Mask) retVal |= 0x20;
+      if(inVal & ArdA6Mask) retVal |= 0x40;
+      if(inVal & ArdA7Mask) retVal |= 0x80;
+      break;
+
+    case ArdD2D5:
+      inVal = NrfGpioPtr1->IN;
+      if(inVal & ArdD2Mask) retVal |= 0x01;
+      if(inVal & ArdD3Mask) retVal |= 0x02;
+      if(inVal & ArdD4Mask) retVal |= 0x04;
+      if(inVal & ArdD5Mask) retVal |= 0x08;
+      break;
+  }
+
+  return(retVal);
+}
+
+
+void      nRF52840Gpio::write(GpioExtRefPtr refPtr, GpioExtValPtr valPtr)
+{
+  ((nrfGpioPtr) refPtr->ioPtr)->OUTSET = valPtr->value & refPtr->pins;
+  ((nrfGpioPtr) refPtr->ioPtr)->OUTCLR = ~valPtr->value & refPtr->pins;
+  if(refPtr->next == NULL) return;
+  ((nrfGpioPtr) refPtr->next->ioPtr)->OUTSET = valPtr->next->value & refPtr->next->pins;
+  ((nrfGpioPtr) refPtr->next->ioPtr)->OUTCLR = ~valPtr->next->value & refPtr->next->pins;
+}
+
+void      nRF52840Gpio::set(GpioExtRefPtr refPtr)
+{
+  ((nrfGpioPtr) refPtr->ioPtr)->OUTSET = refPtr->pins;
+  if(refPtr->next == NULL) return;
+  ((nrfGpioPtr) refPtr->next->ioPtr)->OUTSET = refPtr->next->pins;
+}
+
+void      nRF52840Gpio::clr(GpioExtRefPtr refPtr)
+{
+  ((nrfGpioPtr) refPtr->ioPtr)->OUTCLR = refPtr->pins;
+  if(refPtr->next == NULL) return;
+  ((nrfGpioPtr) refPtr->next->ioPtr)->OUTCLR = refPtr->next->pins;
+}
+
+
+
+void      nRF52840Gpio::writeArd(ArdMask ardMask, dword value)
+{
+  dword   set0 = 0, set1 = 0;
+  dword   clr0 = 0, clr1 = 0;
+
+  switch(ardMask)
+  {
+    case ArdA0A3:
+      if(value & 0x01) set0 |= ArdA0Mask;
+      else clr0 |= ArdA0Mask;
+      if(value & 0x02) set0 |= ArdA1Mask;
+      else clr0 |= ArdA1Mask;
+      if(value & 0x04) set0 |= ArdA2Mask;
+      else clr0 |= ArdA2Mask;
+      if(value & 0x08) set0 |= ArdA3Mask;
+      else clr0 |= ArdA3Mask;
+
+      NrfGpioPtr0->OUTSET = set0;
+      NrfGpioPtr0->OUTCLR = clr0;
+      break;
+
+    case ArdA4A7:
+      if(value & 0x01) set0 |= ArdA4Mask;
+      else clr0 |= ArdA4Mask;
+      if(value & 0x02) set0 |= ArdA5Mask;
+      else clr0 |= ArdA5Mask;
+      if(value & 0x04) set0 |= ArdA6Mask;
+      else clr0 |= ArdA6Mask;
+      if(value & 0x08) set0 |= ArdA7Mask;
+      else clr0 |= ArdA7Mask;
+
+      NrfGpioPtr0->OUTSET = set0;
+      NrfGpioPtr0->OUTCLR = clr0;
+      break;
+
+    case ArdA0A7:
+      if(value & 0x01) set0 |= ArdA0Mask;
+      else clr0 |= ArdA0Mask;
+      if(value & 0x02) set0 |= ArdA1Mask;
+      else clr0 |= ArdA1Mask;
+      if(value & 0x04) set0 |= ArdA2Mask;
+      else clr0 |= ArdA2Mask;
+      if(value & 0x08) set0 |= ArdA3Mask;
+      else clr0 |= ArdA3Mask;
+      if(value & 0x01) set0 |= ArdA4Mask;
+      else clr0 |= ArdA4Mask;
+      if(value & 0x02) set0 |= ArdA5Mask;
+      else clr0 |= ArdA5Mask;
+      if(value & 0x04) set0 |= ArdA6Mask;
+      else clr0 |= ArdA6Mask;
+      if(value & 0x08) set0 |= ArdA7Mask;
+      else clr0 |= ArdA7Mask;
+
+      NrfGpioPtr0->OUTSET = set0;
+      NrfGpioPtr0->OUTCLR = clr0;
+      break;
+
+    case ArdD2D5:
+      if(value & 0x01) set1 |= ArdD2Mask;
+      else clr1 |= ArdD2Mask;
+      if(value & 0x02) set1 |= ArdD3Mask;
+      else clr1 |= ArdD3Mask;
+      if(value & 0x04) set1 |= ArdD4Mask;
+      else clr1 |= ArdD4Mask;
+      if(value & 0x08) set1 |= ArdD5Mask;
+      else clr1 |= ArdD5Mask;
+
+      NrfGpioPtr1->OUTSET = set1;
+      NrfGpioPtr1->OUTCLR = clr1;
+      break;
+  }
+}
+
+
+  // ----------------------------------------------------------------------------
+  // Ereignisbearbeitung und Interrupts
+  // ----------------------------------------------------------------------------
+  //
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Gpio/nRF52840Gpio.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Gpio/nRF52840Gpio.h
new file mode 100644
index 0000000000000000000000000000000000000000..addf5ad533ddfecabd2c540824238c278a117998
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Gpio/nRF52840Gpio.h
@@ -0,0 +1,181 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Gpio.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   29. Juni 2021
+//
+
+#ifndef NRF52840GPIO_H
+#define NRF52840GPIO_H
+
+#include "Arduino.h"
+#include "arduinoDefs.h"
+#include "IntrfGpio.h"
+
+#ifndef nrfGpioDef
+// ----------------------------------------------------------------------------
+typedef struct _nrfGpio
+{
+  volatile  dword Reserve01;                // 000
+  volatile  dword OUT;                      // 004
+  volatile  dword OUTSET;                   // 008
+  volatile  dword OUTCLR;                   // 00C
+  volatile  dword IN;                       // 010
+  volatile  dword DIR;                      // 014
+  volatile  dword DIRSET;                   // 018
+  volatile  dword DIRCLR;                   // 01C
+  volatile  dword LATCH;                    // 020
+  volatile  dword DETECTMODE;               // 024
+  volatile  dword Reserve02[118];           // 026
+  volatile  dword PIN_CNF[32];              // 200
+} nrfGpio, *nrfGpioPtr;
+
+#define NrfGpioBase   0x50000000
+#define NrfGpioBase0  0x50000500
+#define NrfGpioPtr0   ((nrfGpioPtr) NrfGpioBase0)
+#define NrfGpioBase1  0x50000800
+#define NrfGpioPtr1   ((nrfGpioPtr) NrfGpioBase1)
+
+#define GpioPinCnf_DIR        ((dword) 0x00000001)
+
+#define GpioPinCnf_INPUT      ((dword) 0x00000001 << 1)
+
+#define GpioPinCnf_PULL(x)    ((dword) x << 2)
+#define GpioPullDown          1
+#define GpioPullUp            3
+
+#define GpioPinCnf_DRIVE(x)   ((dword) x << 8)
+#define GpioDriveS0S1         0
+#define GpioDriveH0S1         1
+#define GpioDriveS0H1         2
+#define GpioDriveH0H1         3
+#define GpioDriveD0S1         4
+#define GpioDriveD0H1         5
+#define GpioDriveS0D1         6
+#define GpioDriveH0D1         7
+
+#define GpioPinCnf_SENSE(x)   ((dword) x << 16)
+#define GpioSenseHigh         2
+#define GpioSenseLow          3
+
+#define nrfGpioDef
+// ----------------------------------------------------------------------------
+#endif
+
+#define P0(x) (x)
+#define P1(x) (32+x)
+
+#ifdef smnNANOBLE33
+// ----------------------------------------------------------------------------
+#define ArdA0Bit    4
+#define ArdA1Bit    5
+#define ArdA2Bit    30
+#define ArdA3Bit    29
+#define ArdA4Bit    31
+#define ArdA5Bit    2
+#define ArdA6Bit    28
+#define ArdA7Bit    3
+
+#define ArdA0   P0(4)
+#define ArdA1   P0(5)
+#define ArdA2   P0(30)
+#define ArdA3   P0(29)
+#define ArdA4   P0(31)
+#define ArdA5   P0(2)
+#define ArdA6   P0(28)
+#define ArdA7   P0(3)
+
+#define ArdA0Mask   (1 << 4)
+#define ArdA1Mask   (1 << 5)
+#define ArdA2Mask   (1 << 30)
+#define ArdA3Mask   (1 << 29)
+#define ArdA4Mask   (1 << 31)
+#define ArdA5Mask   (1 << 2)
+#define ArdA6Mask   (1 << 28)
+#define ArdA7Mask   (1 << 3)
+
+
+#define ArdD2       P1(11)
+#define ArdD3       P1(12)
+#define ArdD4       P1(15)
+#define ArdD5       P1(13)
+#define ArdD6       P0(14)
+#define ArdD7       P0(23)
+#define ArdD8       P1(21)
+#define ArdD9       P0(27)
+#define ArdD10      P1(2)
+#define ArdD11      P1(1)
+#define ArdD12      P1(8)
+#define ArdD13      P0(13)
+
+#define ArdD2Mask   (1 << 11)
+#define ArdD3Mask   (1 << 12)
+#define ArdD4Mask   (1 << 15)
+#define ArdD5Mask   (1 << 13)
+#define ArdD6Mask   (1 << 14)
+#define ArdD7Mask   (1 << 23)
+#define ArdD8Mask   (1 << 21)
+#define ArdD9Mask   (1 << 27)
+#define ArdD10Mask  (1 << 2)
+#define ArdD11Mask  (1 << 1)
+#define ArdD12Mask  (1 << 8)
+#define ArdD13Mask  (1 << 13)
+
+// ----------------------------------------------------------------------------
+#endif
+
+class nRF52840Gpio : IntrfGpio
+{
+private:
+  // --------------------------------------------------------------------------
+  // lokale Variablen
+  // --------------------------------------------------------------------------
+  //
+  nrfGpioPtr  gpioPtr;
+
+public:
+  // --------------------------------------------------------------------------
+  // Konstruktoren
+  // --------------------------------------------------------------------------
+  //
+  nRF52840Gpio();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  dword     getCnfValue(unsigned int cnfBits);
+  GpioError config(int nr, unsigned int cnfBits, GpioExtRefPtr refPtr);
+  GpioError config(int nrFrom, int nrTo, unsigned int cnfBits, GpioExtRefPtr refPtr);
+  GpioError config(GpioExtMask mask, unsigned int cnfBits, GpioExtRefPtr refPtr);
+
+  GpioError configArd(ArdMask ardMask, unsigned int cnfBits);
+
+  // --------------------------------------------------------------------------
+  // Anwendungsfunktionen
+  // --------------------------------------------------------------------------
+  //
+  void      read(GpioExtRefPtr ioRef, GpioExtValPtr valPtr);
+  dword     readArd(ArdMask ardMask);
+
+  void      write(GpioExtRefPtr refPtr, GpioExtValPtr valPtr);
+  void      writeArd(ArdMask ardMask, dword value);
+  void      set(GpioExtRefPtr refPtr);
+  void      clr(GpioExtRefPtr refPtr);
+
+  // ----------------------------------------------------------------------------
+  // Ereignisbearbeitung und Interrupts
+  // ----------------------------------------------------------------------------
+  //
+
+  // --------------------------------------------------------------------------
+  // Debugging und globale Variablen
+  // --------------------------------------------------------------------------
+  //
+
+};
+
+#endif //NRF52840GPIO_H
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Radio/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Radio/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..a18f8f0f08e3ee567cac8bd59e8774c14b3effe7
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Radio/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "nRF52840Radio",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Radio/nRF52840Radio.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Radio/nRF52840Radio.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5639480f98f812bc2170a24674d34534dd95d5e6
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Radio/nRF52840Radio.cpp
@@ -0,0 +1,943 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Radio.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#include "Arduino.h"
+#include "nRF52840Radio.h"
+#include <string.h>
+
+// ----------------------------------------------------------------------------
+// Initialisierungen
+// ----------------------------------------------------------------------------
+
+nRF52840Radio::nRF52840Radio()
+{
+  NrfRadioPtr->TASKS_DISABLE;   // Sender/Empfänger abgeschaltet
+#ifdef nrfPowerDCDCEN
+  *nrfPowerDCDCEN = 1;
+#endif
+#ifdef nrfClockTASKS_HFCLKSTART
+  *nrfClockTASKS_HFCLKSTART = 1;
+#endif
+  NrfRadioPtr->POWER = 0;
+  NrfRadioPtr->POWER = 1;
+
+  irqCounter = 0;
+  trfMode = txmBase;
+  statisticPtr = NULL;
+  recMode = true;
+  pmPtr = (bcPduPtr) pduMem;
+  psPtr = (bcPduPtr) pduSentS;
+  pePtr = (bcPduPtr) pduSentE;
+  eadM = false;
+  nakM = false;
+  comFin = false;
+  comError = false;
+  newValues = false;
+
+  memset(statList,0,NrOfTxModes * sizeof(TxStatistics));
+}
+
+// ----------------------------------------------------------------------------
+// Konfiguration
+// ----------------------------------------------------------------------------
+
+// Allgemeine Vorbereitungen
+//
+void  nRF52840Radio::begin()
+{
+  instPtr0 = this;      // Verzweigung im statischen Bereich setzen
+
+  NrfRadioPtr->INTENCLR = 0xFFFFFFFF;   // Vorsichtshalber keine Interrupts
+
+  // Interruptvektor setzen
+  //
+  __NVIC_SetVector((IRQn_Type) 1, (dword) nRF52840Radio::irqHandler0);
+  __NVIC_SetPriority((IRQn_Type) 1, 1);
+  __NVIC_EnableIRQ((IRQn_Type) 1);
+}
+
+// Setzen der Zugriffsadresse
+//
+void  nRF52840Radio::setAccessAddress(dword addr)
+{
+  dword prefix = addr >> 24;
+  dword base = addr << 8;
+
+  cfgData.base0   = NrfRadioPtr->BASE0        = base;
+  cfgData.prefix0 = NrfRadioPtr->PREFIX0      = prefix;
+  cfgData.txAddr  = NrfRadioPtr->TXADDRESS    = 0;
+  cfgData.rxAddr  = NrfRadioPtr->RXADDRESSES  = 0x01;
+}
+
+// Telegrammparameter setzen
+//
+void  nRF52840Radio::setPacketParms(blePduType type)
+{
+  switch(type)
+  {
+    case bptAdv:
+      cfgData.pCnf0     = NrfRadioPtr->PCNF0        = PCNF0_LFLEN(8) | PCNF0_S0LEN(1) | PCNF0_S1LEN(0);
+      cfgData.pCnf1     = NrfRadioPtr->PCNF1        = PCNF1_MAXLEN(42) | PCNF1_BALEN(3) | PCNF1_WHITEEN(1);
+      cfgData.modeCnf0  = NrfRadioPtr->MODECNF0     = 1;
+      cfgData.crcCnf    = NrfRadioPtr->CRCCNF       = CRCCNF_LEN(3) | CRCCNF_SKIPADDR(1);
+      cfgData.crcPoly   = NrfRadioPtr->CRCPOLY      = PolynomCRC;
+      cfgData.crcInit   = NrfRadioPtr->CRCINIT      = AdvStartCRC;
+      cfgData.packetPtr = NrfRadioPtr->PACKETPTR    = (dword) pduMem;
+      cfgData.mode      = NrfRadioPtr->MODE         = 3;
+      cfgData.dacnf     = NrfRadioPtr->DACNF        = 0x0FF00;
+      break;
+
+    case bptAux:
+      break;
+  }
+}
+
+
+// ----------------------------------------------------------------------------
+// Steuerfunktionen und gezielte Prozessorzugriffe
+// ----------------------------------------------------------------------------
+
+// Schalten des Bewerbungskanals
+//
+void nRF52840Radio::setChannel(int nr)
+{
+  cfgData.frequency = NrfRadioPtr->FREQUENCY = channelList[nr].freq;
+  cfgData.whiteInit = NrfRadioPtr->DATAWHITEIV = channelList[nr].idx;
+}
+
+// ----------------------------------------------------------------------------
+//                      S e n d e n
+// ----------------------------------------------------------------------------
+
+// Einstellen der Sendeleistung
+//
+void  nRF52840Radio::setPower(int DBm)
+{
+  cfgData.txPower = NrfRadioPtr->TXPOWER = DBm;
+}
+// Senden eines Telegramms
+// Es wird davon ausgeganen, das der Radio-Zustand = DISABLED ist
+//
+int nRF52840Radio::sendSync(bcPduPtr inPduPtr, TxStatePtr refState)
+{
+  int   retv = 0;
+  NrfRadioPtr->INTENCLR = 0xFFFFFFFF;
+  NrfRadioPtr->EVENTS_READY = 0;
+  NrfRadioPtr->EVENTS_END = 0;
+  memcpy((void *)pduMem, (void *)inPduPtr, sizeof(bcPdu));  // Daten kopieren
+  if(refState != NULL)
+    refState->prgLoopPrep = retv = 3 + sizeof(bcPdu);
+  NrfRadioPtr->TASKS_TXEN = 1;                  // Starten des Anlaufes
+  while(NrfRadioPtr->EVENTS_READY != 1) retv++; // Warten bis angelaufen
+  if(refState != NULL)
+    refState->evtLoopRampUp = retv - 3;
+  NrfRadioPtr->TASKS_START = 1;                 // Starten des Sendevorgangs
+  while(NrfRadioPtr->EVENTS_END != 1) retv++;   // Warten bis gesendet
+  NrfRadioPtr->TASKS_DISABLE = 1;               // Sender abschalten
+  if(refState != NULL)
+  {
+    refState->evtLoopTrans = retv - refState->evtLoopRampUp;
+    refState->txBufferPtr = pduMem;
+  }
+  return(retv);
+}
+
+void  nRF52840Radio::send(bcPduPtr inPduPtr, TxMode txMode)
+{
+  send(inPduPtr, NULL, txMode, false);
+}
+
+void  nRF52840Radio::send(bcPduPtr inPduPtrE, bcPduPtr inPduPtrS, TxMode txMode, bool inNewValues)
+{
+  NrfRadioPtr->INTENCLR         = 0xFFFFFFFF;
+  NrfRadioPtr->EVENTS_READY     = 0;
+  NrfRadioPtr->EVENTS_END       = 0;
+  NrfRadioPtr->EVENTS_DISABLED  = 0;
+  NrfRadioPtr->EVENTS_RXREADY   = 0;
+  NrfRadioPtr->EVENTS_TXREADY   = 0;
+  NrfRadioPtr->EVENTS_ADDRESS   = 0;
+
+  // TODO
+  // Das muss Alles noch einmal überarbeitet werden.
+  // Hier stecken zu viele Redundanzen drin, Altlast aus diversen Tests mit der Hardware
+
+  memcpy((void *)pduMem, (void *)inPduPtrE, sizeof(bcPdu));    // Daten in Funkpuffer kopieren
+
+  memcpy((void *)pduSentE, (void *)inPduPtrE, sizeof(bcPdu));  // Daten in extra Puffer kopieren
+  // Die übergebenen Daten werden in einen Extrapuffer kopiert zur Entkopplung für eventuelle
+  // lokale Modifikationen
+
+  if(inPduPtrS != NULL)                             // Falls Daten für eine Antwort gegeben sind
+    memcpy((void *)pduSentS, (void *)inPduPtrS, sizeof(bcPdu));// Daten in extra Puffer kopieren
+  // Die übergebenen Daten werden in einen Extrapuffer kopiert zur Entkopplung für eventuelle
+  // lokale Modifikationen
+
+  comFin    = false;
+  comError  = false;
+  newValues = inNewValues;
+
+  trfMode = txMode;
+
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+  statisticPtr = &statList[(int) txMode];
+  statisticPtr->mode = txMode;
+  memset(statisticPtr->memDumpRec,0,16);
+  memset(statisticPtr->memDumpSnd,0,16);
+#endif
+
+  switch(txMode)
+  {
+    case txmPoll:
+      recMode = false;
+      NrfRadioPtr->SHORTS = NrfScTXREADY_START | NrfScEND_DISABLE | NrfScDISABLED_RXEN | NrfScRXREADY_START;
+      NrfRadioPtr->INTENSET = NrfIntRXREADY;
+      NrfRadioPtr->TASKS_TXEN = 1;
+      break;
+
+    case txmResp:
+    case txmRespE:
+      recMode = true;
+      NrfRadioPtr->SHORTS = NrfScREADY_START;
+      NrfRadioPtr->INTENSET = NrfIntEND;
+      NrfRadioPtr->TASKS_RXEN = 1;
+      break;
+
+    case txmBase:
+      NrfRadioPtr->SHORTS = NrfScREADY_START | NrfScEND_DISABLE;
+      NrfRadioPtr->TASKS_TXEN = 1;
+      break;
+
+    case txmRepStart:
+      NrfRadioPtr->SHORTS = NrfScREADY_START;
+      NrfRadioPtr->TASKS_TXEN = 1;
+      break;
+
+    case txmRepCont:
+      NrfRadioPtr->SHORTS = 0;
+      NrfRadioPtr->TASKS_START = 1;
+      break;
+
+    case txmRepEnd:
+      NrfRadioPtr->SHORTS = NrfScEND_DISABLE;
+      NrfRadioPtr->TASKS_START = 1;
+      break;
+
+    case txmReadPrep:
+      NrfRadioPtr->SHORTS = NrfScTXREADY_START | NrfScEND_DISABLE | NrfScDISABLED_RXEN;
+      NrfRadioPtr->TASKS_TXEN = 1;
+      break;
+
+    case txmReadS:
+      NrfRadioPtr->SHORTS = NrfScTXREADY_START | NrfScEND_DISABLE | NrfScDISABLED_RXEN | NrfScRXREADY_START;
+      NrfRadioPtr->INTENSET = NrfIntADDRESS;
+      NrfRadioPtr->TASKS_TXEN = 1;
+      break;
+
+    case txmRead:
+      NrfRadioPtr->SHORTS = NrfScTXREADY_START | NrfScEND_DISABLE | NrfScDISABLED_RXEN | NrfScRXREADY_START;
+      NrfRadioPtr->INTENSET = NrfIntADDRESS | NrfIntEND;
+      NrfRadioPtr->TASKS_TXEN = 1;
+      break;
+  }
+}
+
+void nRF52840Radio::disable(TxMode txMode)
+{
+  switch(txMode)
+   {
+     case txmBase:
+       break;
+
+     case txmRepStart:
+       break;
+
+     case txmRepCont:
+       break;
+
+     case txmRepEnd:
+       break;
+
+     case txmReadPrep:
+       break;
+
+     case txmRead:
+       NrfRadioPtr->TASKS_DISABLE = 1;
+       break;
+
+     case txmPoll:
+       NrfRadioPtr->INTENCLR = 0xFFFFFFFF;
+       NrfRadioPtr->EVENTS_DISABLED = 0;
+       NrfRadioPtr->TASKS_DISABLE = 1;
+       break;
+
+     case txmResp:
+     case txmRespE:
+       NrfRadioPtr->TASKS_DISABLE = 1;
+       break;
+   }
+  NrfRadioPtr->SHORTS = 0;
+}
+
+bool nRF52840Radio::disabled(TxMode txMode)
+{
+  bool retv = false;
+
+  switch(txMode)
+   {
+     case txmBase:
+       break;
+
+     case txmRepStart:
+       break;
+
+     case txmRepCont:
+       break;
+
+     case txmRepEnd:
+       break;
+
+     case txmReadPrep:
+       break;
+
+     case txmRead:
+     case txmPoll:
+       if(NrfRadioPtr->EVENTS_DISABLED == 1)
+       {
+         NrfRadioPtr->EVENTS_DISABLED = 0;
+         retv = true;
+       }
+       else
+       {
+         if(NrfRadioPtr->STATE == NrfStDISABLED)
+           retv = true;
+       }
+       break;
+
+     case txmResp:
+     case txmRespE:
+       if(NrfRadioPtr->EVENTS_DISABLED == 1)
+       {
+         NrfRadioPtr->EVENTS_DISABLED = 0;
+         retv = true;
+       }
+       else
+       {
+         if(NrfRadioPtr->STATE == NrfStDISABLED)
+           retv = true;
+       }
+       break;
+   }
+  return(retv);
+}
+
+bool nRF52840Radio::fin(TxMode txMode, bool *crcErr)
+{
+  bool retv = false;
+
+  *crcErr = comError;
+
+  switch(txMode)
+   {
+     case txmBase:
+       break;
+
+     case txmRepStart:
+       break;
+
+     case txmRepCont:
+       break;
+
+     case txmRepEnd:
+       break;
+
+     case txmReadPrep:
+       break;
+
+     case txmReadS:
+       retv = comFin;
+       break;
+
+     case txmRead:
+       /*
+       if(NrfRadioPtr->EVENTS_END == 1)
+       {
+         NrfRadioPtr->EVENTS_END = 0;
+         retv = true;
+       }
+       else
+       {
+         if(NrfRadioPtr->STATE == NrfStRXIDLE)
+           retv = true;
+       }
+       */
+       retv = comFin;
+       break;
+
+     case txmPoll:
+       retv = comFin;
+       break;
+
+     case txmResp:
+       retv = comFin;
+       break;
+
+     case txmRespE:
+       if(NrfRadioPtr->STATE == NrfStDISABLED && !recMode)
+         retv = true;
+       break;
+   }
+  return(retv);
+}
+
+void nRF52840Radio::cont(TxMode txMode)
+{
+  switch(txMode)
+   {
+     case txmBase:
+       break;
+
+     case txmRepStart:
+       break;
+
+     case txmRepCont:
+       break;
+
+     case txmRepEnd:
+       break;
+
+     case txmReadPrep:
+       break;
+
+     case txmRead:
+       comFin = false;
+       NrfRadioPtr->TASKS_START = 1;
+       break;
+   }
+}
+
+int   nRF52840Radio::getRecData(bcPduPtr data, TxMode txMode, int max)
+{
+  byte *bPtr = (byte *) data;
+  int retv = 0;
+
+  switch(txMode)
+  {
+    case txmResp:
+      data->head = pduSentE[0];
+      retv = data->len  = pduSentE[1];
+
+      for(int i = 2; i < (retv + 2); i++)
+      {
+        if(i == max) break;
+        bPtr[i] = pduSentE[i];
+      }
+
+      break;
+
+    case txmBase:
+      break;
+
+    case txmRepStart:
+      break;
+
+    case txmRepCont:
+      break;
+
+    case txmRepEnd:
+      break;
+
+    case txmReadPrep:
+      break;
+
+    case txmRead:
+      break;
+  }
+
+
+  return(retv);
+}
+
+
+
+// ----------------------------------------------------------------------------
+//                      E m p f a n g e n
+// ----------------------------------------------------------------------------
+
+// Starten des Datenempfangs
+//
+int nRF52840Radio::startRec()
+{
+  int   retv;
+  NrfRadioPtr->INTENCLR = 0xFFFFFFFF;
+  NrfRadioPtr->EVENTS_READY = 0;
+  NrfRadioPtr->EVENTS_END = 0;
+  NrfRadioPtr->EVENTS_ADDRESS = 0;
+  NrfRadioPtr->EVENTS_PAYLOAD = 0;
+  NrfRadioPtr->EVENTS_CRCOK = 0;
+  NrfRadioPtr->EVENTS_CRCERROR = 0;
+  NrfRadioPtr->TASKS_RXEN = 1;                  // Anlauf Empfänger starten
+  retv = 8;
+  while(NrfRadioPtr->EVENTS_READY != 1) retv++; // Warten bis angelaufen
+  NrfRadioPtr->TASKS_START = 1;                 // Starten des Empfangs
+  return(retv + 1);
+}
+
+// Fortsetzen des Datenempfangs
+//
+int nRF52840Radio::contRec()
+{
+  NrfRadioPtr->EVENTS_END = 0;
+  NrfRadioPtr->EVENTS_ADDRESS = 0;
+  NrfRadioPtr->EVENTS_PAYLOAD = 0;
+  NrfRadioPtr->EVENTS_CRCOK = 0;
+  NrfRadioPtr->EVENTS_CRCERROR = 0;
+  NrfRadioPtr->TASKS_START = 1;                 // Starten des Empfangs
+  return(6);
+}
+
+// Beenden des Datenempfangs
+//
+int nRF52840Radio::endRec()
+{
+  int   retv;
+  NrfRadioPtr->EVENTS_DISABLED = 0;
+  NrfRadioPtr->EVENTS_END = 0;
+  NrfRadioPtr->EVENTS_ADDRESS = 0;
+  NrfRadioPtr->EVENTS_PAYLOAD = 0;
+  NrfRadioPtr->EVENTS_CRCOK = 0;
+  NrfRadioPtr->EVENTS_CRCERROR = 0;
+  NrfRadioPtr->TASKS_DISABLE = 1;               // Anlauf Empfänger beenden
+  retv = 7;
+  while(NrfRadioPtr->EVENTS_DISABLED != 1) retv++; // Warten bis abgelaufen
+  return(retv);
+}
+
+// Empfangszustand abfragen
+//
+int nRF52840Radio::checkRec()
+{
+  int retv = 0;
+
+  if(NrfRadioPtr->EVENTS_ADDRESS != 0)
+    retv |= RECSTAT_ADDRESS;
+
+  if(NrfRadioPtr->EVENTS_PAYLOAD != 0)
+    retv |= RECSTAT_PAYLOAD;
+
+  if(NrfRadioPtr->EVENTS_END != 0)
+    retv |= RECSTAT_END;
+
+  if(NrfRadioPtr->CRCSTATUS != 0)
+    retv |= RECSTAT_CRCOK;
+
+  if(NrfRadioPtr->EVENTS_DISABLED != 0)
+    retv |= RECSTAT_DISABLED;
+
+  return(retv);
+}
+
+int   nRF52840Radio::getRecData(bcPduPtr data, int max)
+{
+  int retv;
+  byte *bPtr = (byte *) data;
+
+  data->head = pduMem[0];
+  retv = data->len  = pduMem[1];
+
+  for(int i = 2; i < (retv + 2); i++)
+  {
+    if(i == max) break;
+    bPtr[i] = pduMem[i];
+  }
+
+  return(retv);
+}
+
+// ----------------------------------------------------------------------------
+// Interruptverarbeitung
+// ----------------------------------------------------------------------------
+//
+
+nRF52840Radio *nRF52840Radio::instPtr0 = NULL;
+
+void nRF52840Radio::irqHandler0()
+{
+  if(instPtr0 == NULL) return;
+  instPtr0->irqCounter++;
+  instPtr0->irqHandler();
+}
+
+void nRF52840Radio::irqHandler()
+{
+  statisticPtr->interrupts++;
+
+  switch(trfMode)
+  {
+    // ------------------------------------------------------------------------
+    case txmRead:               // Empty Polling für Master
+    // ------------------------------------------------------------------------
+
+      if((NrfRadioPtr->STATE & 0x08) != 0)  // Noch im Sendemodus
+      { // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_ADDRESS == 1)  // AC-Adr gesendet
+        {
+          NrfRadioPtr->EVENTS_ADDRESS = 0;    // nur quittieren
+        }
+
+        if(NrfRadioPtr->EVENTS_END == 1)      // Senden fertig
+        {
+          NrfRadioPtr->EVENTS_END = 0;        // nur quittieren
+          statisticPtr->sendings++;
+          memcpy(statisticPtr->memDumpSnd,pduMem,8);
+        }
+      }
+      else                                  // im Empfangsmodus
+      { // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_ADDRESS == 1)  // AC-Adr empfangen
+        {
+          NrfRadioPtr->EVENTS_ADDRESS = 0;    // quittieren
+          NrfRadioPtr->SHORTS = 0;            // Direktverbindungen löschen
+        }
+
+        if(NrfRadioPtr->EVENTS_END == 1)      // Empfang fertig
+        {
+          NrfRadioPtr->EVENTS_END = 0;        // nur quittieren
+          comFin = true;
+          statisticPtr->recs++;
+          memcpy(statisticPtr->memDumpRec,pduMem,8);
+        }
+      }
+
+      break;
+
+    // ------------------------------------------------------------------------
+    case txmPoll:     // Empty Polling und Datenempfang für Master -> DISABLED
+    // ------------------------------------------------------------------------
+      if(NrfRadioPtr->EVENTS_RXREADY == 1)    // Empfang aktiviert
+      {
+        NrfRadioPtr->EVENTS_RXREADY = 0;      // Int quittieren
+
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+        statisticPtr->sendings++;
+        memcpy(statisticPtr->memDumpSnd,pduMem,8);
+#endif
+        NrfRadioPtr->EVENTS_DISABLED = 0;     // Neu erwartet, zurücksetzen
+        NrfRadioPtr->SHORTS = NrfScEND_DISABLE | NrfScRXREADY_START;
+        NrfRadioPtr->INTENSET = NrfIntDISABLED;
+        recMode = true;
+      }
+
+      if(NrfRadioPtr->EVENTS_DISABLED == 1)   // Empfang beendet
+      {
+        NrfRadioPtr->EVENTS_DISABLED = 0;     // Int quittieren
+        comFin = true;
+        if(NrfRadioPtr->CRCSTATUS == 0)
+          comError = true;
+
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+        statisticPtr->recs++;
+        if(comError) statisticPtr->crcErrors++;
+        memcpy(statisticPtr->memDumpRec,pduMem,16);
+        if(!recMode)
+        {
+          // Das darf nicht passieren, sonst neue Int-Verarbeitung erforderlich
+          statisticPtr->intErrors++;
+        }
+#endif
+      }
+      break;
+
+    // ------------------------------------------------------------------------
+    case txmReadS:              // Datenübertragung für Master (Slave->Master)
+    // ------------------------------------------------------------------------
+
+      if(NrfRadioPtr->EVENTS_ADDRESS == 1)    // AC-Adr gesendet
+      {
+        NrfRadioPtr->EVENTS_ADDRESS = 0;      // quittieren
+
+        if((NrfRadioPtr->STATE & 0x08) != 0)  // im Sendezustand
+          break;                              // nichts weiter
+        // --------------------------------------------------------------------
+        else                                  // Im Empfangszustand
+        {
+          NrfRadioPtr->SHORTS = NrfScEND_DISABLE; // automatisch abschalten
+          NrfRadioPtr->INTENCLR = 0xFFFFFFFF;     // und nur noch DISABLED
+          NrfRadioPtr->INTENSET = NrfIntDISABLED; // Interrupt freischalten
+        }
+      }
+
+      if(NrfRadioPtr->EVENTS_DISABLED == 1)   // Übertragung fertig
+      {
+        NrfRadioPtr->EVENTS_DISABLED = 0;     // quittieren
+        comFin = true;
+      }
+
+
+      break;
+
+    // ----------------------------------------------------------------------
+    case txmRespE:              // Empty Polling für Slave
+    // ----------------------------------------------------------------------
+
+        // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_END == 1)        // Übertragung beendet
+        // --------------------------------------------------------------------
+        {
+          NrfRadioPtr->EVENTS_END = 0;          // Event quittieren
+
+          if(recMode)                           // im Empfangsmodus
+          { // ----------------------------------------------------------------
+            NrfRadioPtr->SHORTS = 0;            // keine direkte Kopplung mehr
+
+            statisticPtr->recs++;
+            memcpy(statisticPtr->memDumpRec,pduMem,8);
+
+            // ----------------------------------------------------------------
+            // Reaktion
+            // ----------------------------------------------------------------
+            //
+            if((pduSentE[5] == pduMem[5]) && (pduSentE[6] == pduMem[6]) && (pduSentE[7] == pduMem[7]))
+            {
+              // Die richtige Protokollumgebung (z.B. Soaap)
+              //
+              if(pduSentE[2] != pduMem[2])
+              {
+                // aber die falsche Adresse
+                // Datenempfang fortsetzen
+                statisticPtr->wrongs++;
+                NrfRadioPtr->TASKS_START = 1;
+              }
+              else
+              { // richtige Adresse, Antwort schicken
+                // ------------------------------------------------------------
+                statisticPtr->pollNaks++;
+
+                // zunächst alle Funk-Interrupts sperren
+                NrfRadioPtr->INTENCLR = 0xFFFFFFFF;
+
+                // Interrupt freigeben für "Abgeschaltet"
+                NrfRadioPtr->INTENSET = NrfIntDISABLED;
+
+                // Empfangsbetrieb abschalten
+                NrfRadioPtr->TASKS_DISABLE = 1;
+              }
+
+            }
+            else
+            {
+              // Fremde Umgebung (nicht akzeptierte PDU)
+              // Datenempfang fortsetzen
+              statisticPtr->aliens++;
+              NrfRadioPtr->TASKS_START = 1;
+            }
+          }
+          else                                      // im Sendemodus
+          { // ----------------------------------------------------------------
+
+
+          }
+        }
+
+        // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_DISABLED == 1)       // ausgeschaltet
+        // --------------------------------------------------------------------
+        {
+          NrfRadioPtr->EVENTS_DISABLED = 0;         // quittieren
+
+          if(recMode)
+          {
+            // zunächst alle Funk-Interrupts sperren
+            NrfRadioPtr->INTENCLR = 0xFFFFFFFF;
+
+            // Interrupt freigeben für "Abgeschaltet"
+            NrfRadioPtr->INTENSET = NrfIntDISABLED;
+
+            // Kopplung automatisch starten und abschalten nach Ende
+            NrfRadioPtr->SHORTS = NrfScREADY_START | NrfScEND_DISABLE;
+
+            NrfRadioPtr->TASKS_TXEN = 1;             // Sender einschalten
+            recMode = false;
+
+            // Daten in Funkpuffer kopieren
+            memcpy((void *)pduMem, (void *)pduSentE, sizeof(bcPdu));
+          }
+          else
+          {
+            NrfRadioPtr->SHORTS = 0;
+            statisticPtr->sendings++;
+          }
+        }
+
+        break;
+
+      // ----------------------------------------------------------------------
+      case txmResp:               // Datenübertragung Slave
+      // ----------------------------------------------------------------------
+
+        // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_END == 1)        // Übertragung beendet
+                                                // Polling-Daten empfangen
+        // --------------------------------------------------------------------
+        {
+          NrfRadioPtr->EVENTS_END = 0;          // Event quittieren
+
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+          statisticPtr->recs++;
+          memcpy(statisticPtr->memDumpRec,pduMem,8);
+#endif
+
+          if((pduSentE[5] != pduMem[5]) || (pduSentE[6] != pduMem[6]) || (pduSentE[7] != pduMem[7]))
+          {
+            // Das empfangene Telegramm gehört nicht zum eigenen Netzwerk
+            //
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+            statisticPtr->aliens++;
+#endif
+            NrfRadioPtr->SHORTS = 0;            // Keine Direktverbindungen
+            NrfRadioPtr->TASKS_START = 1;       // Datenempfang fortsetzen
+            break;
+          }
+
+          if((pduSentE[2] != pduMem[2]) || ((pduSentE[3] & 0x3F) != (pduMem[3] & 0x3F)))
+          {
+            // Das empfangene Telegramm ist für einen anderen Slave oder Bereich
+            //
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+            statisticPtr->wrongs++;
+#endif
+            NrfRadioPtr->SHORTS = 0;            // Keine Direktverbindungen
+            NrfRadioPtr->TASKS_START = 1;       // Datenempfang fortsetzen
+            break;
+            // TODO
+            // Hier wird das Protokoll noch erweitert zum Belauschen anderer Slaves
+          }
+
+          eadM = ((pduMem[3] & SOAAP_EADR) != 0); // Merker für Empfangspolling
+          nakM = ((pduMem[3] & SOAAP_NAK) != 0);  // Merker für NAK-Polling
+
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+          if(nakM)
+            statisticPtr->pollNaks++;
+          else
+            statisticPtr->pollAcks++;
+#endif
+          if(eadM)
+          {
+            // Empfangsaufforderung
+            // Eadr-Nak-Daten in Funkpuffer kopieren
+            //
+            memcpy((void *)pduMem, (void *)pduSentE, sizeof(bcPdu));
+          }
+          else
+          {
+            // Sendeaufforderung
+            // Polling-Steuerdaten in Empfangspuffer und
+            // Sadr-Ack-Daten in Funkpuffer kopieren
+            //
+            memcpy((void *)pduSentE, (void *)pduMem, sizeof(bcPdu));
+            memcpy((void *)pduMem, (void *)pduSentS, sizeof(bcPdu));
+          }
+
+          // Setzen der Direktverbinder auf vollständigen Durchlauf bis Ende der Sendung
+          // ACHTUNG! Freigegebene Interrupts treten auf, auch wenn sie zu einer
+          //          Direktverbindung gehören.
+          //
+          NrfRadioPtr->SHORTS = NrfScDISABLED_TXEN | NrfScREADY_START | NrfScEND_DISABLE;
+          NrfRadioPtr->EVENTS_READY = 0;        // Evt. hängendes Ereignis löschen
+          NrfRadioPtr->INTENSET = NrfIntREADY;  // Int zur Vorbereitung des Sendeabschlusses
+          NrfRadioPtr->TASKS_DISABLE = 1;       // Abschalten des Empfangsbetriebs
+          break;
+        }
+
+        // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_READY == 1)      // Bereit zum Senden
+        // --------------------------------------------------------------------
+        {
+          NrfRadioPtr->EVENTS_READY = 0;        // Event quittieren
+
+          // Vorbereiten auf das Ende der Sendung mit Abschalten
+          // NrfScREADY_START | NrfScEND_DISABLE sind weiter wirksam
+          //
+          NrfRadioPtr->SHORTS = NrfScREADY_START | NrfScEND_DISABLE;
+          NrfRadioPtr->EVENTS_DISABLED = 0;       // Evt. hängendes Ereignis löschen
+          recMode = false;
+          NrfRadioPtr->INTENSET = NrfIntDISABLED; // Int zum Sendeabschluss
+          break;
+        }
+
+        // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_DISABLED == 1)   // Sendevorgang beendet
+        // --------------------------------------------------------------------
+        {
+          NrfRadioPtr->EVENTS_DISABLED = 0;     // Event quittieren
+          comFin = true;
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+          statisticPtr->sendings++;
+          memcpy(statisticPtr->memDumpSnd,pduMem,16);
+#endif
+        }
+
+        break;
+
+  }
+}
+
+// --------------------------------------------------------------------------
+// Datenzugriffe
+// --------------------------------------------------------------------------
+//
+int   nRF52840Radio::getStatistics(TxStatisticsPtr dest)
+{
+  int retv = 0;
+
+  *dest = *statisticPtr;
+  return(retv);
+}
+
+int nRF52840Radio::getState()
+{
+  return(NrfRadioPtr->STATE);
+}
+
+// ----------------------------------------------------------------------------
+//                      D e b u g - H i l f e n
+// ----------------------------------------------------------------------------
+//
+int   nRF52840Radio::getPduMem(byte *dest, int start, int end)
+{
+  int i,j;
+
+  j = 0;
+
+  for(i = start; i < end; i++)
+  {
+    dest[j++] = pduMem[i];
+  }
+  return(j);
+}
+
+int   nRF52840Radio::getPduSent(byte *dest, int start, int end)
+{
+  int i,j;
+
+  j = 0;
+
+  for(i = start; i < end; i++)
+  {
+    dest[j++] = pduSentE[i];
+  }
+  return(j);
+}
+
+
+
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Radio/nRF52840Radio.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Radio/nRF52840Radio.h
new file mode 100644
index 0000000000000000000000000000000000000000..0e61246043b8b3bd78928b3e33e31f81e180d011
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Radio/nRF52840Radio.h
@@ -0,0 +1,313 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Radio.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#ifndef NRF52840RADIO_H
+#define NRF52840RADIO_H
+
+#include "arduinoDefs.h"
+#include "bleSpec.h"
+#include "IntrfRadio.h"
+
+#define nRF52840RadioDEB
+
+// ----------------------------------------------------------------------------
+
+typedef struct _NRF_RADIO_Type
+{
+  volatile  dword  TASKS_TXEN;
+  volatile  dword  TASKS_RXEN;
+  volatile  dword  TASKS_START;
+  volatile  dword  TASKS_STOP;
+  volatile  dword  TASKS_DISABLE;
+  volatile  dword  TASKS_RSSISTART;
+  volatile  dword  TASKS_RSSISTOP;
+  volatile  dword  TASKS_BCSTART;
+  volatile  dword  TASKS_BCSTOP;
+  volatile  dword  TASKS_EDSTART;
+  volatile  dword  TASKS_EDSTOP;
+  volatile  dword  TASKS_CCASTART;
+  volatile  dword  TASKS_CCASTOP;
+  volatile  dword  RESERVED0[51];
+  volatile  dword  EVENTS_READY;
+  volatile  dword  EVENTS_ADDRESS;
+  volatile  dword  EVENTS_PAYLOAD;
+  volatile  dword  EVENTS_END;
+  volatile  dword  EVENTS_DISABLED;
+  volatile  dword  EVENTS_DEVMATCH;
+  volatile  dword  EVENTS_DEVMISS;
+  volatile  dword  EVENTS_RSSIEND;
+  volatile  dword  RESERVED1[2];
+  volatile  dword  EVENTS_BCMATCH;
+  volatile  dword  RESERVED2;
+  volatile  dword  EVENTS_CRCOK;
+  volatile  dword  EVENTS_CRCERROR;
+  volatile  dword  EVENTS_FRAMESTART;
+  volatile  dword  EVENTS_EDEND;
+  volatile  dword  EVENTS_EDSTOPPED;
+  volatile  dword  EVENTS_CCAIDLE;
+  volatile  dword  EVENTS_CCABUSY;
+  volatile  dword  EVENTS_CCASTOPPED;
+  volatile  dword  EVENTS_RATEBOOST;
+  volatile  dword  EVENTS_TXREADY;
+  volatile  dword  EVENTS_RXREADY;
+  volatile  dword  EVENTS_MHRMATCH;
+  volatile  dword  RESERVED3[2];
+  volatile  dword  EVENTS_SYNC;
+  volatile  dword  EVENTS_PHYEND;
+  volatile  dword  RESERVED4[36];
+  volatile  dword  SHORTS;
+  volatile  dword  RESERVED5[64];
+  volatile  dword  INTENSET;
+  volatile  dword  INTENCLR;
+  volatile  dword  RESERVED6[61];
+  volatile  dword  CRCSTATUS;
+  volatile  dword  RESERVED7;
+  volatile  dword  RXMATCH;
+  volatile  dword  RXCRC;
+  volatile  dword  DAI;
+  volatile  dword  PDUSTAT;
+  volatile  dword  RESERVED8[59];
+  volatile  dword  PACKETPTR;
+  volatile  dword  FREQUENCY;
+  volatile  dword  TXPOWER;
+  volatile  dword  MODE;
+  volatile  dword  PCNF0;
+  volatile  dword  PCNF1;
+  volatile  dword  BASE0;
+  volatile  dword  BASE1;
+  volatile  dword  PREFIX0;
+  volatile  dword  PREFIX1;
+  volatile  dword  TXADDRESS;
+  volatile  dword  RXADDRESSES;
+  volatile  dword  CRCCNF;
+  volatile  dword  CRCPOLY;
+  volatile  dword  CRCINIT;
+  volatile  dword  RESERVED9;
+  volatile  dword  TIFS;
+  volatile  dword  RSSISAMPLE;
+  volatile  dword  RESERVED10;
+  volatile  dword  STATE;
+  volatile  dword  DATAWHITEIV;
+  volatile  dword  RESERVED11[2];
+  volatile  dword  BCC;
+  volatile  dword  RESERVED12[39];
+  volatile  dword  DAB[8];
+  volatile  dword  DAP[8];
+  volatile  dword  DACNF;
+  volatile  dword  MHRMATCHCONF;
+  volatile  dword  MHRMATCHMAS;
+  volatile  dword  RESERVED13;
+  volatile  dword  MODECNF0;
+  volatile  dword  RESERVED14[3];
+  volatile  dword  SFD;
+  volatile  dword  EDCNT;
+  volatile  dword  EDSAMPLE;
+  volatile  dword  CCACTRL;
+  volatile  dword  RESERVED15[611];
+  volatile  dword  POWER;
+} *nrfRadioPtr;
+
+
+
+#define NrfRadioBase    0x40001000
+#define NrfRadioPtr     ((nrfRadioPtr) NrfRadioBase)
+
+#ifndef NrfPowerBase
+#define NrfPowerBase    0x40000000
+#define nrfPowerDCDCEN  ((dword *) 0x40000578)
+#endif
+
+#ifndef NrfClockBase
+#define NrfClockBase    0x40000000
+#define nrfClockTASKS_HFCLKSTART  ((dword *) 0x40000000)
+#endif
+
+// Direktverbindungen (shortcuts) zwischen events und Tasks
+//
+#define NrfScREADY_START    0x00000001
+#define NrfScEND_DISABLE    0x00000002
+#define NrfScDISABLED_TXEN  0x00000004
+#define NrfScDISABLED_RXEN  0x00000008
+#define NrfScTXREADY_START  0x00040000
+#define NrfScRXREADY_START  0x00080000
+
+// Interrupts
+//
+#define NrfIntREADY         0x00000001
+#define NrfIntADDRESS       0x00000002
+#define NrfIntPAYLOAD       0x00000004
+#define NrfIntEND           0x00000008
+#define NrfIntDISABLED      0x00000010
+#define NrfIntRSSIEND       0x00000080
+#define NrfIntTXREADY       0x00200000
+#define NrfIntRXREADY       0x00400000
+
+// Zustände
+//
+#define NrfStDISABLED       0
+#define NrfStRXRU           1
+#define NrfStRXIDLE         2
+#define NrfStRX             3
+#define NrfStRXDISABLE      4
+#define NrfStTXRU           9
+#define NrfStTXIDLE         10
+#define NrfStTX             11
+#define NrfStTXDISABLE      12
+
+// Festlegungen für die Paketkonfigurationsregister
+//
+
+#define PCNF0_LFLEN(x)    x
+// Anzahl der Bits im Längenfeld (0-15)
+
+#define PCNF0_S0LEN(x)    (x << 8)
+// Länge des Header0 (S0) in Bytes (0 oder 1)
+
+#define PCNF0_S1LEN(x)    (x << 16)
+// Länge des S1-Feldes in Bit (0 bis 15)
+
+#define PCNF1_MAXLEN(x)   x
+// Maximale Telegrammlänge (0 bis 255)
+
+#define PCNF1_BALEN(x)    (x << 16)
+// Basislänge der Zugriffsadresse (Access Address, 2-4)
+
+#define PCNF1_WHITEEN(x)  (x << 25)
+// Whitening (Bitmischung) Ein/Aus (1/0)
+
+// Festlegungen für die CRC-Generierung
+//
+
+#define CRCCNF_LEN(x)     x
+// Anzahl der Bytes für CRC (0-3)
+
+#define CRCCNF_SKIPADDR(x)  (x << 8)
+// Zugriffsadresse (Access Address) nicht im CRC (1), im CRC (0)
+
+
+typedef struct _nrf52840Cfg
+{
+  dword   pCnf0;
+  dword   pCnf1;
+  dword   whiteInit;
+  dword   modeCnf0;
+  dword   crcPoly;
+  dword   crcInit;
+  dword   crcCnf;
+  dword   packetPtr;
+  dword   frequency;
+  dword   txPower;
+  dword   mode;
+  dword   dacnf;
+  dword   rxAddrEn;
+  dword   base0;
+  dword   prefix0;
+  dword   txAddr;
+  dword   rxAddr;
+
+}nrf52840Cfg, *nrf52840CfgPtr;
+
+// ----------------------------------------------------------------------------
+
+class nRF52840Radio : IntrfRadio
+{
+private:
+  // --------------------------------------------------------------------------
+  // Lokale Daten
+  // --------------------------------------------------------------------------
+  //
+  byte        pduMem[256];
+  byte        pduSentE[256];
+  byte        pduSentS[256];
+
+  bcPduPtr    pmPtr;
+  bcPduPtr    pePtr;
+  bcPduPtr    psPtr;
+
+  nrf52840Cfg cfgData;
+
+  bool        recMode;
+  bool        eadM;
+  bool        nakM;
+  bool        comFin;
+  bool        comError;
+  bool        newValues;
+
+  dword       irqCounter;
+  TxMode      trfMode;
+
+  TxStatistics    statList[NrOfTxModes];
+  TxStatisticsPtr statisticPtr;
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen der Basis-Klasse
+  // --------------------------------------------------------------------------
+
+  nRF52840Radio();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  void  begin();
+  void  setAccessAddress(dword addr); // Setzen der Zugriffsadresse
+  void  setPacketParms(blePduType type);
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+  void  setChannel(int nr);           // Schalten physikalischer Kanal
+  int   sendSync(bcPduPtr inPduPtr, TxStatePtr refState);
+
+  void  send(bcPduPtr inPduPtr, TxMode txMode);
+  void  send(bcPduPtr inPduPtrE, bcPduPtr inPduPtrS, TxMode txMode, bool newValues);
+  int   getRecData(bcPduPtr data, TxMode txMode, int max);  // Empfangene Daten lesen
+
+  void  disable(TxMode txMode);
+  bool  disabled(TxMode txMode);      // Abfrage, ob ausgeschaltet
+  void  cont(TxMode txMode);
+  bool  fin(TxMode txMode, bool *err);
+                                      // Senden eines Telegramms (und warten)
+  int   startRec();                   // Datenempfang starten
+  int   contRec();                    // Datenempfang fortsetzen
+  int   endRec();                     // Datenempfang beenden
+  int   checkRec();                   // Zustand Datenempfang feststellen
+  int   getRecData(bcPduPtr data, int max);  // Empfangene Daten lesen
+
+  void  setPower(int DBm);            // Leistung des Senders in DBm
+
+  void  readCheckCfg();               // Konfigurationsdaten auslesen
+
+  static  nRF52840Radio *instPtr0;
+  static  void irqHandler0();
+
+  void    irqHandler();
+
+  // --------------------------------------------------------------------------
+  // Datenzugriffe
+  // --------------------------------------------------------------------------
+  //
+  int   getStatistics(TxStatisticsPtr dest);
+  int   getState();
+
+  // ----------------------------------------------------------------------------
+  //                      D e b u g - H i l f e n
+  // ----------------------------------------------------------------------------
+  //
+  int   getPduMem(byte *dest, int start, int end);
+  int   getPduSent(byte *dest, int start, int end);
+
+
+};
+
+
+#endif // NRF52840RADIO_H
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Ser/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Ser/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..fa5d17537ce11418ad11ee8b2689c1ae0ba6a20b
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Ser/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "nRF52840Ser",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Ser/nRF52840Ser.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Ser/nRF52840Ser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..86b66bb6cd5ba3f8e3a58c8587d7df52eac240f5
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Ser/nRF52840Ser.cpp
@@ -0,0 +1,323 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Ser.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#include "nRF52840Ser.h"
+#include <string.h>
+
+// ----------------------------------------------------------------------------
+// Initialisierungen
+// ----------------------------------------------------------------------------
+
+nRF52840Ser::nRF52840Ser()
+{
+  serPtr      = NULL;
+  instPtr0    = NULL;
+  irqCounter  = 0;
+  curIntEn    = 0;
+  curIRQ      = 0;
+  lastError   = 0;
+  cntError    = 0;
+}
+
+dword speedArr[18] =
+{
+    0x0004F000, 0x0009D000, 0x0013B000,
+    0x00275000, 0x003B0000, 0x004EA000,
+    0x0075F000, 0x00800000, 0x009D5000,
+    0x00E50000, 0x00EBF000, 0x013A9000,
+    0x01D7E000, 0x03AFB000, 0x04000000,
+    0x075F7000, 0x0EBED000, 0x10000000
+};
+
+// ----------------------------------------------------------------------------
+// Konfiguration
+// ----------------------------------------------------------------------------
+//
+void nRF52840Ser::begin(SerParamsPtr inParPtr, IntrfBuf *bufferIf)
+{
+  nrfGpioPtr  gpioPtr;
+  dword       regVal;
+
+  // Setzen des Peripheriezeigers anhand der Instanz
+  // und Initialisieren weiterer Variablen/Zeiger
+  //
+  serPtr = NrfSerPtr0;
+  clrAllEvents();
+  instPtr0  = this;
+  curIRQ    = 2;
+
+  // Interruptvektor setzen
+  //
+  __NVIC_SetVector((IRQn_Type) 2, (dword) nRF52840Ser::irqHandler0);
+  __NVIC_SetPriority((IRQn_Type) 2, 1);
+  __NVIC_EnableIRQ((IRQn_Type) 2);
+
+  // Alternative Peripherie (gleiche ID, also Alles) abschalten
+  //
+  serPtr->ENABLE = SerDisable;
+
+
+  // TxD
+  // -------------------------------------------------
+  //
+  // Pins zuweisen und initialisieren
+  //
+  if(inParPtr->txdPort == 1)
+  {
+    regVal = 32 + inParPtr->txdPin;
+    gpioPtr = NrfGpioPtr1;
+  }
+  else
+  {
+    regVal = inParPtr->txdPin;
+    gpioPtr = NrfGpioPtr0;
+  }
+
+
+  // Connect (hoechstwertiges Bit) beruecksichtigen
+  //
+  serPtr->PSEL_TXD = regVal | 0x7FFFFFC0;
+
+  // Zugewiesenen Pin als Ausgang schalten und Treibermodus setzen
+  // Laut Datenblatt ist das entsprechende Bit im Konfigurationsregister
+  // mit dem DIR-Register physikalisch verbunden
+  //
+  if(inParPtr->type == stStd)
+    regVal = GpioPinCnf_DRIVE(GpioDriveS0S1);
+  else if(inParPtr->type == stPow)
+    regVal = GpioPinCnf_DRIVE(GpioDriveH0H1);
+  else
+    regVal = GpioPinCnf_DRIVE(GpioDriveH0D1);
+
+  gpioPtr->PIN_CNF[inParPtr->txdPin] = regVal | GpioPinCnf_DIROUT;
+
+  // RXD
+  // -------------------------------------------------
+  //
+  if(inParPtr->rxdPort == 1)
+  {
+    regVal = 32 + inParPtr->rxdPin;
+    gpioPtr = NrfGpioPtr1;
+  }
+  else
+  {
+    regVal = inParPtr->rxdPin;
+    gpioPtr = NrfGpioPtr0;
+  }
+
+  // Connect (hoechstwertiges Bit) beruecksichtigen
+  //
+  serPtr->PSEL_RXD = regVal | 0x7FFFFFC0;
+
+  // Zugewiesenen Pin als Eingang schalten und Treibermodus setzen
+  // Laut Datenblatt ist das entsprechende Bit im Konfigurationsregister
+  // mit dem DIR-Register physikalisch verbunden
+  //
+  gpioPtr->PIN_CNF[inParPtr->rxdPin] = GpioPinCnf_PULL(GpioPullUp);
+
+  // Bitrate einstellen
+  //
+  regVal = speedArr[inParPtr->speed];
+  serPtr->BAUDRATE = regVal;
+  serPtr->SHORTS = 0;
+
+  // Interrupts freischalten
+  //
+  curIntEn = (SerInt_TXDRDY | SerInt_RXDRDY | SerInt_ERROR);
+  serPtr->INTENSET = curIntEn;
+
+  // Und bereit machen
+  //
+  serPtr->ENABLE = SerEnable;
+  txdFin = true;
+
+  bufIf = bufferIf;
+  delay(10);
+}
+
+
+// ----------------------------------------------------------------------------
+// Steuerfunktionen, gezielte Prozessorzugriffe und Hilfsfunktionen
+// ----------------------------------------------------------------------------
+//
+void nRF52840Ser::clrAllEvents()
+{
+  serPtr->EVENTS_RXDRDY     = 0;
+  serPtr->EVENTS_TXDRDY     = 0;
+  serPtr->EVENTS_ERROR      = 0;
+}
+
+// Fortsetzen des Interrupt-Sendebetriebs
+//
+void nRF52840Ser::resuSend()
+{
+  byte  td;
+
+  if(!txdFin) return;
+  if(bufIf == NULL) return;
+  if(!bufIf->getByteSnd(&td)) return;
+
+  txdFin = false;
+  serPtr->EVENTS_TXDRDY = 0;
+  serPtr->TXD = td;
+}
+
+// Starten des Sendebetriebs
+//
+void nRF52840Ser::startSend()
+{
+  serPtr->TASKS_STARTTX = 1;
+}
+
+// Anhalten des Sendebetriebs
+//
+void nRF52840Ser::stopSend()
+{
+  serPtr->TASKS_STOPTX = 1;
+}
+
+// Starten des Empfangsbetriebs
+//
+void nRF52840Ser::startRec()
+{
+  serPtr->TASKS_STARTRX = 1;
+}
+
+// Anhalten des Empfangsbetriebs
+//
+void nRF52840Ser::stopRec()
+{
+  serPtr->TASKS_STOPRX = 1;
+}
+
+
+// Bedingtes Ausgeben eines Zeichens
+//
+bool nRF52840Ser::condSend(byte c)
+{
+  if(!txdFin) return(false);
+
+  txdFin = false;
+  serPtr->EVENTS_TXDRDY = 0;
+  serPtr->TXD = c;
+  return(true);
+}
+
+// ----------------------------------------------------------------------------
+// Ereignisbearbeitung und Interrupts
+// ----------------------------------------------------------------------------
+//
+nRF52840Ser *nRF52840Ser::instPtr0 = NULL;
+
+void nRF52840Ser::irqHandler0()
+{
+  if(instPtr0 == NULL) return;
+  instPtr0->irqCounter++;
+  instPtr0->irqHandler();
+}
+
+
+  // --------------------------------------------------------------------------
+  // Interrupts (Ereignisbehandlung)
+  // --------------------------------------------------------------------------
+  //
+void nRF52840Ser::irqHandler()
+{
+  byte  b;
+
+  if(serPtr->EVENTS_TXDRDY != 0)
+  {
+    serPtr->EVENTS_TXDRDY = 0;
+    if(bufIf == NULL) return;
+
+    if(!bufIf->getByteSnd(&b))
+      txdFin = true;
+    else
+      serPtr->TXD = b;
+  }
+  else if(serPtr->EVENTS_RXDRDY != 0)
+  {
+    serPtr->EVENTS_RXDRDY = 0;
+    b = serPtr->RXD;
+    if(bufIf == NULL) return;
+    bufIf->putByteRec(b);
+  }
+  else if(serPtr->EVENTS_ERROR != 0)
+  {
+    serPtr->EVENTS_ERROR = 0;
+    cntError++;
+    lastError = serPtr->ERRORSRC;
+    anyError |= lastError;
+  }
+}
+
+// --------------------------------------------------------------------------
+// Datenzugriffe
+// --------------------------------------------------------------------------
+//
+// Letzten Fehler lesen (Bits)
+//
+int   nRF52840Ser::getLastError()
+{
+  return(lastError);
+}
+
+// Alle vorgekommenen Fehlerbits
+//
+int   nRF52840Ser::getAnyError()
+{
+  return(anyError);
+}
+
+// Anzahl der Fehler lesen
+//
+dword nRF52840Ser::getErrCount()
+{
+  return(cntError);
+}
+
+
+
+// ----------------------------------------------------------------------------
+//                      D e b u g - H i l f e n
+// ----------------------------------------------------------------------------
+//
+dword nRF52840Ser::getIrqCount()
+{
+  return(irqCounter);
+}
+
+void nRF52840Ser::resetIrqList()
+{
+  irqIdx = 0;
+  firstRead = true;
+
+  for(int i = 0; i < 8; i++)
+    irqList[i] = 0;
+}
+
+void nRF52840Ser::getIrqList(char *dest)
+{
+  int destIdx = 0;
+
+  for(int i = 0; i < 8; i++)
+  {
+    if(irqList[i] == 0) break;
+
+    dest[destIdx++] = ' ';
+    dest[destIdx++] = irqList[i] + 0x30;
+  }
+
+  dest[destIdx] = '\0';
+}
+
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Ser/nRF52840Ser.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Ser/nRF52840Ser.h
new file mode 100644
index 0000000000000000000000000000000000000000..37fcae4b19bc8ec15a2a83040f6f086d35e68efd
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Ser/nRF52840Ser.h
@@ -0,0 +1,234 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Ser.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#ifndef NRF52840SER_H
+#define NRF52840SER_H
+
+#include "Arduino.h"
+#include "arduinoDefs.h"
+#include "IntrfBuf.h"
+#include "IntrfSerial.h"
+
+// ----------------------------------------------------------------------------
+
+typedef struct _nrfSer
+{
+  volatile  dword  TASKS_STARTRX;           // 000
+  volatile  dword  TASKS_STOPRX;            // 004
+  volatile  dword  TASKS_STARTTX;           // 008
+  volatile  dword  TASKS_STOPTX;            // 00C
+  volatile  dword  Reserve01[3];            // 010
+  volatile  dword  TASKS_SUSPEND;           // 01C
+  volatile  dword  Reserve02[56];           // 020
+  volatile  dword  EVENTS_CTS;              // 100
+  volatile  dword  EVENTS_NCTS;             // 104
+  volatile  dword  EVENTS_RXDRDY;           // 108
+  volatile  dword  Reserve03[4];            // 10C
+  volatile  dword  EVENTS_TXDRDY;           // 11C
+  volatile  dword  Reserve04;               // 120
+  volatile  dword  EVENTS_ERROR;            // 124
+  volatile  dword  Reserve05[7];            // 128
+  volatile  dword  EVENTS_RXTO;             // 144
+  volatile  dword  Reserve06[46];           // 148
+  volatile  dword  SHORTS;                  // 200
+  volatile  dword  Reserve07[64];           // 204
+  volatile  dword  INTENSET;                // 304
+  volatile  dword  INTENCLR;                // 308
+  volatile  dword  Reserve08[93];           // 30C
+  volatile  dword  ERRORSRC;                // 480
+  volatile  dword  Reserve09[31];           // 484
+  volatile  dword  ENABLE;                  // 500
+  volatile  dword  Reserve10;               // 504
+  volatile  dword  PSEL_RTS;                // 508
+  volatile  dword  PSEL_TXD;                // 50C
+  volatile  dword  PSEL_CTS;                // 510
+  volatile  dword  PSEL_RXD;                // 514
+  volatile  dword  RXD;                     // 518
+  volatile  dword  TXD;                     // 51C
+  volatile  dword  Reserve11;               // 520
+  volatile  dword  BAUDRATE;                // 524
+  volatile  dword  Reserve12[17];           // 528
+  volatile  dword  CONFIG;                  // 56C
+} nrfSer, *nrfSerPtr;
+
+#define NrfSerBase0   0x40002000
+#define NrfSerPtr0    ((nrfSerPtr) NrfSerBase0)
+
+#ifndef nrfGpioDef
+
+typedef struct _nrfGpio
+{
+  volatile  dword Reserve01;                // 000
+  volatile  dword OUT;                      // 004
+  volatile  dword OUTSET;                   // 008
+  volatile  dword OUTCLR;                   // 00C
+  volatile  dword IN;                       // 010
+  volatile  dword DIR;                      // 014
+  volatile  dword DIRSET;                   // 018
+  volatile  dword DIRCLR;                   // 01C
+  volatile  dword LATCH;                    // 020
+  volatile  dword DETECTMODE;               // 024
+  volatile  dword Reserve02[118];           // 026
+  volatile  dword PIN_CNF[32];              // 200
+} nrfGpio, *nrfGpioPtr;
+
+#define NrfGpioBase   0x50000000
+#define NrfGpioBase0  0x50000500
+#define NrfGpioPtr0   ((nrfGpioPtr) NrfGpioBase0)
+#define NrfGpioBase1  0x50000800
+#define NrfGpioPtr1   ((nrfGpioPtr) NrfGpioBase1)
+
+#define GpioPinCnf_DIROUT     ((dword) 0x00000001)
+
+#define GpioPinCnf_DISBUF     ((dword) 0x00000002)
+
+#define GpioPinCnf_PULL(x)    ((dword) x << 2)
+#define GpioPullDown          1
+#define GpioPullUp            3
+
+#define GpioPinCnf_DRIVE(x)   ((dword) x << 8)
+#define GpioDriveS0S1         0
+#define GpioDriveH0S1         1
+#define GpioDriveS0H1         2
+#define GpioDriveH0H1         3
+#define GpioDriveD0S1         4
+#define GpioDriveD0H1         5
+#define GpioDriveS0D1         6
+#define GpioDriveH0D1         7
+
+#define GpioPinCnf_SENSE(x)   ((dword) x << 16)
+#define GpioSenseHigh         2
+#define GpioSenseLow          3
+
+#define nrfGpioDef
+#endif
+
+// Festlegungen für die Paketkonfigurationsregister
+//
+
+#define SerInt_RXDRDY     ((dword) 0x00000001 << 2)
+// Interrupt für Event RXDRDY
+
+#define SerInt_TXDRDY     ((dword) 0x00000001 << 7)
+// Interrupt für Event TXDRDY
+
+#define SerInt_ERROR      ((dword) 0x00000001 << 9)
+// Interrupt für Event ERROR
+
+
+#define SerEnable   4
+#define SerDisable  0
+
+// Bit-Masken fuer Kommunikationsbedingungen
+//
+#define BM_REC_NOT_COND 0x00
+// Keine Bedingungen beim Empfang
+#define BM_REC_END_CHR  0x01
+// Empfang Stoppen beim Eintreffen des vorgegebenen Zeichens
+#define BM_REC_RINGBUF  0x02
+// Receive characters in ring buffer
+#define BM_SND_RINGBUF  0x04
+// Transmit characters via ring buffer
+
+
+
+// ----------------------------------------------------------------------------
+
+class nRF52840Ser : IntrfSerial
+{
+private:
+  // --------------------------------------------------------------------------
+  // Lokale Daten und Funktionen
+  // --------------------------------------------------------------------------
+  //
+  nrfSerPtr     serPtr;
+  dword         irqCounter;
+
+  int           lastError;
+  int           anyError;
+  dword         cntError;
+
+  int           curIRQ;
+  dword         curIntEn;
+
+  IntrfBuf      *bufIf;
+  bool          txdFin;       // TRUE = Sendevorgang beendet
+
+  void clrAllEvents();
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen der Basis-Klasse
+  // --------------------------------------------------------------------------
+
+  nRF52840Ser();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  void begin(SerParamsPtr serParPtr, IntrfBuf *bufferIf);
+
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+  void resuSend();    // Fortsetzen des Interrupt-Sendebetriebs
+  void startSend();   // Starten des Sendebetriebs
+  void stopSend();    // Anhalten des Sendebetriebs
+
+  void startRec();    // Starten des Empfangsbetriebs
+  void stopRec();     // Anhalten des Empfangsbetriebs
+
+
+  // --------------------------------------------------------------------------
+  // Datenzugriffe
+  // --------------------------------------------------------------------------
+  //
+  bool condSend(byte c);  // Bedingtes Senden eines Zeichens
+
+  int   getLastError();   // Letzten Fehler lesen (Bits)
+  int   getAnyError();    // Alle vorgekommenen Fehlerbits
+  dword getErrCount();    // Anzahl der Fehler lesen
+
+
+  // ----------------------------------------------------------------------------
+  // Ereignisbearbeitung und Interrupts
+  // ----------------------------------------------------------------------------
+  //
+  static  nRF52840Ser *instPtr0;
+  static  void irqHandler0();
+
+  void    irqHandler();
+
+  // --------------------------------------------------------------------------
+  // lokale Variablen
+  // --------------------------------------------------------------------------
+  //
+
+
+  // ----------------------------------------------------------------------------
+  //                      D e b u g - H i l f e n
+  // ----------------------------------------------------------------------------
+  //
+  int           irqIdx;
+  int           irqList[8];
+
+  byte          extraValue;
+  bool          firstRead;
+
+  dword   getIrqCount();
+  void    resetIrqList();
+  void    getIrqList(char *dest);
+
+};
+
+#endif // NRF52840SER_H
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Twi/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Twi/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..16ae3922cdc02214ba2709422d1f9acfa86d132d
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Twi/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "nRF52840Twi",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Twi/nRF52840Twi.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Twi/nRF52840Twi.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..81fdb263721b902935a3a21f952b214796e8c9d5
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Twi/nRF52840Twi.cpp
@@ -0,0 +1,586 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Radio.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#include "nRF52840Twi.h"
+#include <string.h>
+
+// ----------------------------------------------------------------------------
+// Initialisierungen
+// ----------------------------------------------------------------------------
+
+nRF52840Twi::nRF52840Twi()
+{
+  twiPtr      = NULL;
+  instPtr0    = NULL;
+  instPtr1    = NULL;
+  irqCounter  = 0;
+}
+
+// ----------------------------------------------------------------------------
+// Konfiguration
+// ----------------------------------------------------------------------------
+//
+TwiError nRF52840Twi::begin(TwiParamsPtr inParPtr)
+{
+  TwiError    retv;
+  nrfGpioPtr  gpioPtr;
+  dword       regVal;
+
+  retv = TEnoError;
+
+  params = *inParPtr;
+
+  // Setzen des Peripheriezeigers anhand der Instanz
+  // und Initialisieren weiterer Variablen/Zeiger
+  //
+  if(inParPtr->inst == 0)
+  {
+    twiPtr    = NrfTwiPtr0;
+    clrAllEvents();
+    instPtr0  = this;
+    curIRQ    = 3;
+
+    // Interruptvektor setzen
+    //
+    __NVIC_SetVector((IRQn_Type) 3, (dword) nRF52840Twi::irqHandler0);
+    __NVIC_SetPriority((IRQn_Type) 3, 1);
+    __NVIC_EnableIRQ((IRQn_Type) 3);
+  }
+  else
+  {
+    twiPtr    = NrfTwiPtr1;
+    clrAllEvents();
+    instPtr1  = this;
+    curIRQ    = 4;
+
+    // Interruptvektor setzen
+    //
+    __NVIC_SetVector((IRQn_Type) 4, (dword) nRF52840Twi::irqHandler1);
+    __NVIC_SetPriority((IRQn_Type) 4, 1);
+    __NVIC_EnableIRQ((IRQn_Type) 4);
+  }
+
+  // Alternative Peripherie (gleiche ID, also Alles) abschalten
+  //
+  twiPtr->ENABLE = TwiDisable;
+
+
+  // Takt
+  // -------------------------------------------------
+  //
+  // Pins zuweisen und initialisieren
+  //
+  if(inParPtr->clkPort == 1)
+  {
+    regVal = 32 + inParPtr->clkPin;
+    gpioPtr = NrfGpioPtr1;
+  }
+  else
+  {
+    regVal = inParPtr->clkPin;
+    gpioPtr = NrfGpioPtr0;
+  }
+
+  // Connect (hoechstwertiges Bit) beruecksichtigen
+  //
+  twiPtr->PSEL_SCL = regVal | 0x7FFFFFC0;
+
+  // Zugewiesenen Pin als Eingang schalten und Treibermodus setzen
+  // Laut Datenblatt ist das entsprechende Bit im Konfigurationsregister
+  // mit dem DIR-Register physikalisch verbunden
+  //
+  gpioPtr->PIN_CNF[inParPtr->clkPin] = GpioPinCnf_DRIVE(GpioDriveS0D1);
+
+  // Daten
+  // -------------------------------------------------
+  //
+  if(inParPtr->dataPort == 1)
+  {
+    regVal = 32 + inParPtr->dataPin;
+    gpioPtr = NrfGpioPtr1;
+  }
+  else
+  {
+    regVal = inParPtr->dataPin;
+    gpioPtr = NrfGpioPtr0;
+  }
+
+  // Connect (hoechstwertiges Bit) beruecksichtigen
+  //
+  twiPtr->PSEL_SDA = regVal | 0x7FFFFFC0;
+
+  // Zugewiesenen Pin als Eingang schalten und Treibermodus setzen
+  // Laut Datenblatt ist das entsprechende Bit im Konfigurationsregister
+  // mit dem DIR-Register physikalisch verbunden
+  //
+  gpioPtr->PIN_CNF[inParPtr->dataPin] = GpioPinCnf_DRIVE(GpioDriveS0D1);
+
+  // Frequenz einstellen
+  //
+  if(inParPtr->speed == Twi100k)
+    regVal = NrfTwi100k;
+  else if(inParPtr->speed == Twi400k)
+    regVal = NrfTwi400k;
+  else
+    regVal = NrfTwi250k;
+
+  twiPtr->FREQUENCY = regVal;
+
+  twiPtr->SHORTS = 0;
+
+  // Interrupts freischalten
+  //
+  curIntEn = (TwiInt_TXDSENT | TwiInt_RXDREADY | TwiInt_ERROR | TwiInt_STOPPED);
+  twiPtr->INTENSET = curIntEn;
+
+  // Und bereit machen
+  //
+  twiPtr->ENABLE = TwiEnable;
+
+  delay(10);
+
+  return(retv);
+}
+
+
+void nRF52840Twi::getParams(TwiParamsPtr parPtr)
+{
+  *parPtr = params;
+}
+
+
+// ----------------------------------------------------------------------------
+// Steuerfunktionen, gezielte Prozessorzugriffe und Hilfsfunktionen
+// ----------------------------------------------------------------------------
+//
+void nRF52840Twi::clrAllEvents()
+{
+  twiPtr->EVENTS_STOPPED    = 0;
+  twiPtr->EVENTS_RXDREADY   = 0;
+  twiPtr->EVENTS_TXDSENT    = 0;
+  twiPtr->EVENTS_SUSPENDED  = 0;
+  twiPtr->EVENTS_BB         = 0;
+  twiPtr->EVENTS_ERROR      = 0;
+}
+
+// ----------------------------------------------------------------------------
+//                      M a s t e r
+// ----------------------------------------------------------------------------
+//
+
+  // --------------------------------------------------------------------------
+  // Datenaustausch
+  // --------------------------------------------------------------------------
+  //
+TwiError nRF52840Twi::sendByte(int adr, TwiBytePtr refByte)
+{
+  TwiError retv = TEnoError;
+  lastError     = 0;
+
+  resetIrqList();
+
+  byteStruPtr     = refByte;
+  twiPtr->ADDRESS = adr;
+
+  byteStruPtr->twiStatus  = TwStWrReq;
+  trfMode                 = ttmWriteByte;
+
+  twiPtr->TASKS_STARTTX   = 1;
+  twiPtr->TXD             = refByte->value;
+
+  return(retv);
+}
+
+TwiError nRF52840Twi::sendByteReg(int adr, int reg, TwiBytePtr refByte)
+{
+  TwiError retv = TEnoError;
+
+  //dynHand = &nRF52840Twi::irqHandler;
+
+  resetIrqList();
+
+  byteStruPtr     = refByte;
+  twiPtr->ADDRESS = adr;
+
+  byteStruPtr->twiStatus  = TwStWrReq;
+  trfMode                 = ttmWriteByteReg;
+  comIdx                  = 1;
+  irqIdx                  = 0;
+
+  twiPtr->TASKS_STARTTX   = 1;
+  twiPtr->TXD             = reg;
+
+  return(retv);
+}
+
+TwiStatus nRF52840Twi::writeByteReg(int adr, int reg, byte value)
+{
+  twiPtr->INTENCLR = curIntEn;
+
+  tmpByte.value = value;
+
+  sendByteReg(adr, reg, &tmpByte);
+
+  while(tmpByte.twiStatus != TwStFin)
+  {
+    irqHandler();
+
+    if(tmpByte.twiStatus & TwStError)
+      break;
+  }
+
+  twiPtr->INTENSET = curIntEn;
+
+  return(tmpByte.twiStatus);
+}
+
+
+TwiError nRF52840Twi::recByteReg(int adr, int reg, TwiBytePtr refByte)
+{
+  TwiError retv = TEnoError;
+
+  //dynHand = &nRF52840Twi::irqHandler;
+
+  byteStruPtr     = refByte;
+  twiPtr->ADDRESS = adr;
+
+  resetIrqList();
+
+  byteStruPtr->twiStatus  = TwStRdReq;
+  trfMode                 = ttmReadByteReg;
+
+  twiPtr->TASKS_STARTTX   = 1;
+  twiPtr->TXD             = reg;
+
+  return(retv);
+}
+
+int nRF52840Twi::readByteReg(int adr, int reg)
+{
+  twiPtr->INTENCLR = curIntEn;
+
+  recByteReg(adr, reg, &tmpByte);
+
+  while(tmpByte.twiStatus != TwStFin)
+  {
+    irqHandler();
+
+    if(tmpByte.twiStatus & TwStError)
+      break;
+  }
+
+  twiPtr->INTENSET = curIntEn;
+
+  if(tmpByte.twiStatus == TwStFin)
+    return(tmpByte.value);
+  else
+    return(-1);
+}
+
+
+TwiError nRF52840Twi::recByteRegSeq(int adr, int reg, TwiByteSeqPtr refByteSeq)
+{
+  TwiError retv = TEnoError;
+
+  byteSeqPtr      = refByteSeq;
+  comIdx          = 0;
+  twiPtr->ADDRESS = adr;
+
+  byteSeqPtr->twiStatus   = TwStRdReq;
+  trfMode                 = ttmReadByteRegSeq;
+
+  twiPtr->TASKS_STARTTX   = 1;
+  twiPtr->TXD             = reg;
+
+  return(retv);
+}
+
+TwiStatus nRF52840Twi::readByteRegSeq(int adr, int reg, TwiByteSeqPtr refByteSeq)
+{
+  byteSeqPtr      = refByteSeq;
+  comIdx          = 0;
+  twiPtr->ADDRESS = adr;
+
+  byteSeqPtr->twiStatus   = TwStRdReq;
+  trfMode                 = ttmReadByteRegSeq;
+
+  twiPtr->INTENCLR        = curIntEn;
+
+  twiPtr->TASKS_STARTTX   = 1;
+  twiPtr->TXD             = reg;
+
+  while(byteSeqPtr->twiStatus != TwStFin)
+  {
+    irqHandler();
+
+    if(byteSeqPtr->twiStatus & TwStError)
+      break;
+  }
+
+  twiPtr->INTENSET        = curIntEn;
+
+  return(byteSeqPtr->twiStatus);
+}
+
+// ----------------------------------------------------------------------------
+// Ereignisbearbeitung und Interrupts
+// ----------------------------------------------------------------------------
+//
+nRF52840Twi *nRF52840Twi::instPtr0 = NULL;
+
+void nRF52840Twi::irqHandler0()
+{
+  if(instPtr0 == NULL) return;
+  instPtr0->irqCounter++;
+  instPtr0->irqHandler();
+}
+
+nRF52840Twi *nRF52840Twi::instPtr1 = NULL;
+
+void nRF52840Twi::irqHandler1()
+{
+  if(instPtr1 == NULL) return;
+  instPtr1->irqCounter++;
+  instPtr1->irqHandler();
+}
+
+
+  // --------------------------------------------------------------------------
+  // Interrupts (Ereignisbehandlung)
+  // --------------------------------------------------------------------------
+  //
+void nRF52840Twi::irqHandler()
+{
+  switch(trfMode)
+  {
+    case ttmWriteByte:
+    // ------------------------------------------------------------------------
+
+      if(twiPtr->EVENTS_ERROR)
+      {
+        twiPtr->EVENTS_ERROR    = 0;
+        lastError               = twiPtr->ERRORSRC;
+        twiPtr->ERRORSRC        = (lastError & 0x06);   // Clear AdrNak/DataNak
+        byteStruPtr->twiStatus  = (TwiStatus) ( (int) TwStError + lastError);
+        twiPtr->TASKS_STOP      = 1;
+
+        irqList[irqIdx++] = 8;
+        return;
+      }
+
+      if(twiPtr->EVENTS_TXDSENT)
+      {
+        twiPtr->EVENTS_TXDSENT   = 0;
+        byteStruPtr->twiStatus   = TwStSent;
+        twiPtr->TASKS_STOP       = 1;
+
+        irqList[irqIdx++] = 1;
+        return;
+      }
+
+      if(twiPtr->EVENTS_STOPPED)
+      {
+        twiPtr->EVENTS_STOPPED  = 0;
+        if(lastError == 0)
+          byteStruPtr->twiStatus  = TwStFin;
+
+        irqList[irqIdx++] = 3;
+        return;
+      }
+
+      break;
+
+    case ttmWriteByteReg:
+    // ------------------------------------------------------------------------
+
+      if(twiPtr->EVENTS_ERROR)
+      {
+        twiPtr->EVENTS_ERROR    = 0;
+        lastError               = twiPtr->ERRORSRC;
+        twiPtr->ERRORSRC        = (lastError & 0x06);   // Clear AdrNak/DataNak
+        byteStruPtr->twiStatus  = (TwiStatus) ( (int) TwStError + lastError);
+        twiPtr->TASKS_STOP      = 1;
+        return;
+      }
+
+      if(twiPtr->EVENTS_TXDSENT)
+      {
+        twiPtr->EVENTS_TXDSENT   = 0;
+        byteStruPtr->twiStatus   = TwStSent;
+        if(comIdx == 1)
+        {
+          comIdx = 0;
+          twiPtr->TXD = byteStruPtr->value;
+        }
+        else
+          twiPtr->TASKS_STOP    = 1;
+        return;
+      }
+
+      if(twiPtr->EVENTS_STOPPED)
+      {
+        twiPtr->EVENTS_STOPPED  = 0;
+        if(lastError == 0)
+          byteStruPtr->twiStatus  = TwStFin;
+        return;
+      }
+
+      break;
+
+    case ttmReadByteReg:
+    // ------------------------------------------------------------------------
+
+      if(twiPtr->EVENTS_ERROR)
+      {
+        twiPtr->EVENTS_ERROR    = 0;
+        lastError               = twiPtr->ERRORSRC;
+        twiPtr->ERRORSRC        = (lastError & 0x06);   // Clear AdrNak/DataNak
+        byteStruPtr->twiStatus  = (TwiStatus) ( (int) TwStError + lastError);
+        twiPtr->TASKS_STOP      = 1;
+
+        irqList[irqIdx++] = 8;
+        return;
+      }
+
+      if(twiPtr->EVENTS_TXDSENT)
+      {
+        twiPtr->EVENTS_TXDSENT  = 0;
+        byteStruPtr->twiStatus  = TwStSent;
+        twiPtr->TASKS_STARTRX   = 1;
+        twiPtr->SHORTS          = 2;
+
+        irqList[irqIdx++] = 1;
+        return;
+      }
+
+      if(twiPtr->EVENTS_STOPPED)
+      {
+        twiPtr->EVENTS_STOPPED  = 0;
+        if(lastError == 0)
+          byteStruPtr->twiStatus  = TwStFin;
+
+        irqList[irqIdx++] = 3;
+        twiPtr->SHORTS = 0;
+        return;
+      }
+
+      if(twiPtr->EVENTS_RXDREADY)
+      {
+        twiPtr->EVENTS_RXDREADY = 0;
+        byteStruPtr->twiStatus  = TwStRecvd;
+        byteStruPtr->value      = twiPtr->RXD;
+
+        irqList[irqIdx++] = 2;
+        return;
+      }
+
+      break;
+
+    case ttmReadByteRegSeq:
+    // ------------------------------------------------------------------------
+
+      if(twiPtr->EVENTS_ERROR)
+      {
+        twiPtr->EVENTS_ERROR    = 0;
+        lastError               = twiPtr->ERRORSRC;
+        twiPtr->ERRORSRC        = (lastError & 0x06);   // Clear AdrNak/DataNak
+        byteSeqPtr->twiStatus   = (TwiStatus) ( (int) TwStError + lastError);
+        twiPtr->TASKS_STOP      = 1;
+        return;
+      }
+
+      if(twiPtr->EVENTS_TXDSENT)
+      {
+        twiPtr->EVENTS_TXDSENT  = 0;
+        byteSeqPtr->twiStatus   = TwStSent;
+        twiPtr->TASKS_STARTRX   = 1;
+        return;
+      }
+
+      if(twiPtr->EVENTS_STOPPED)
+      {
+        twiPtr->EVENTS_STOPPED   = 0;
+        if(lastError == 0)
+          byteSeqPtr->twiStatus  = TwStFin;
+        twiPtr->SHORTS = 0;
+        return;
+      }
+
+      if(twiPtr->EVENTS_RXDREADY)
+      {
+        twiPtr->EVENTS_RXDREADY       = 0;
+        byteSeqPtr->twiStatus         = TwStRecvd;
+        /*
+        if(comIdx == (byteSeqPtr->len - 2))
+          twiPtr->SHORTS              = 2;
+        byteSeqPtr->valueRef[comIdx]  = twiPtr->RXD;
+        if(comIdx < (byteSeqPtr->len - 1))
+          comIdx++;
+        */
+        if(comIdx == (byteSeqPtr->len - 2))
+          twiPtr->SHORTS              = 2;
+
+        lastIn = twiPtr->RXD;
+
+        if(comIdx < (byteSeqPtr->len))
+          byteSeqPtr->valueRef[comIdx]  = lastIn;
+
+        comIdx++;
+        return;
+      }
+
+      break;
+
+  }
+}
+
+// ----------------------------------------------------------------------------
+//                      S l a v e
+// ----------------------------------------------------------------------------
+
+// Starten des Datenempfangs
+//
+
+// ----------------------------------------------------------------------------
+//                      D e b u g - H i l f e n
+// ----------------------------------------------------------------------------
+//
+dword nRF52840Twi::getIrqCount()
+{
+  return(irqCounter);
+}
+
+void nRF52840Twi::resetIrqList()
+{
+  irqIdx = 0;
+  firstRead = true;
+
+  for(int i = 0; i < 8; i++)
+    irqList[i] = 0;
+}
+
+void nRF52840Twi::getIrqList(char *dest)
+{
+  int destIdx = 0;
+
+  for(int i = 0; i < 8; i++)
+  {
+    if(irqList[i] == 0) break;
+
+    dest[destIdx++] = ' ';
+    dest[destIdx++] = irqList[i] + 0x30;
+  }
+
+  dest[destIdx] = '\0';
+}
+
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Twi/nRF52840Twi.h b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Twi/nRF52840Twi.h
new file mode 100644
index 0000000000000000000000000000000000000000..e33ca74b72c254311a695ab5ea7fd3152a8ebf4d
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleMaster_Test_2/src/nRF52840Twi/nRF52840Twi.h
@@ -0,0 +1,250 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Twi.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#ifndef NRF52840TWI_H
+#define NRF52840TWI_H
+
+#include "Arduino.h"
+#include "arduinoDefs.h"
+#include "IntrfTw.h"
+
+// ----------------------------------------------------------------------------
+
+typedef struct _nrfTwi
+{
+  volatile  dword  TASKS_STARTRX;           // 000
+  volatile  dword  Reserve01;               // 004
+  volatile  dword  TASKS_STARTTX;           // 008
+  volatile  dword  Reserve02[2];            // 00C
+  volatile  dword  TASKS_STOP;              // 014
+  volatile  dword  Reserve03;               // 018
+  volatile  dword  TASKS_SUSPEND;           // 01C
+  volatile  dword  TASKS_RESUME;            // 020
+  volatile  dword  Reserve04[56];           // 024
+  volatile  dword  EVENTS_STOPPED;          // 104
+  volatile  dword  EVENTS_RXDREADY;         // 108
+  volatile  dword  Reserve05[4];            // 118
+  volatile  dword  EVENTS_TXDSENT;          // 11C
+  volatile  dword  Reserve06;               // 120
+  volatile  dword  EVENTS_ERROR;            // 124
+  volatile  dword  Reserve07[4];            // 128
+  volatile  dword  EVENTS_BB;               // 138
+  volatile  dword  Reserve08[3];            // 13C
+  volatile  dword  EVENTS_SUSPENDED;        // 148
+  volatile  dword  Reserve09[45];           // 14C
+  volatile  dword  SHORTS;                  // 200
+  volatile  dword  Reserve10[64];           // 204
+  volatile  dword  INTENSET;                // 304
+  volatile  dword  INTENCLR;                // 308
+  volatile  dword  Reserve11[110];          // 30C
+  volatile  dword  ERRORSRC;                // 4C4
+  volatile  dword  Reserve12[14];           // 4C8
+  volatile  dword  ENABLE;                  // 500
+  volatile  dword  Reserve13;               // 504
+  volatile  dword  PSEL_SCL;                // 508
+  volatile  dword  PSEL_SDA;                // 50C
+  volatile  dword  Reserve14[2];            // 510
+  volatile  dword  RXD;                     // 518
+  volatile  dword  TXD;                     // 51C
+  volatile  dword  Reserve15;               // 520
+  volatile  dword  FREQUENCY;               // 524
+  volatile  dword  Reserve16[24];           // 528
+  volatile  dword  ADDRESS;                 // 588
+} nrfTwi, *nrfTwiPtr;
+
+#define NrfTwiBase0   0x40003000
+#define NrfTwiPtr0    ((nrfTwiPtr) NrfTwiBase0)
+#define NrfTwiBase1   0x40004000
+#define NrfTwiPtr1    ((nrfTwiPtr) NrfTwiBase1)
+
+#define NrfTwi100k    0x01980000
+#define NrfTwi250k    0x04000000
+#define NrfTwi400k    0x06680000
+
+typedef enum _TwiTrfMode
+{
+  ttmWriteByte = 1,
+  ttmWriteByteReg,
+  ttmReadByteReg,
+  ttmReadByteRegSeq
+} TwiTrfMode;
+
+#ifndef nrfGpioDef
+
+typedef struct _nrfGpio
+{
+  volatile  dword Reserve01;                // 000
+  volatile  dword OUT;                      // 004
+  volatile  dword OUTSET;                   // 008
+  volatile  dword OUTCLR;                   // 00C
+  volatile  dword IN;                       // 010
+  volatile  dword DIR;                      // 014
+  volatile  dword DIRSET;                   // 018
+  volatile  dword DIRCLR;                   // 01C
+  volatile  dword LATCH;                    // 020
+  volatile  dword DETECTMODE;               // 024
+  volatile  dword Reserve02[118];           // 026
+  volatile  dword PIN_CNF[32];              // 200
+} nrfGpio, *nrfGpioPtr;
+
+#define NrfGpioBase   0x50000000
+#define NrfGpioBase0  0x50000500
+#define NrfGpioPtr0   ((nrfGpioPtr) NrfGpioBase0)
+#define NrfGpioBase1  0x50000800
+#define NrfGpioPtr1   ((nrfGpioPtr) NrfGpioBase1)
+
+#define GpioPinCnf_DIR        ((dword) 0x00000001)
+
+#define GpioPinCnf_INPUT      ((dword) 0x00000001 << 1)
+
+#define GpioPinCnf_PULL(x)    ((dword) x << 2)
+#define GpioPullDown          1
+#define GpioPullUp            3
+
+#define GpioPinCnf_DRIVE(x)   ((dword) x << 8)
+#define GpioDriveS0S1         0
+#define GpioDriveH0S1         1
+#define GpioDriveS0H1         2
+#define GpioDriveH0H1         3
+#define GpioDriveD0S1         4
+#define GpioDriveD0H1         5
+#define GpioDriveS0D1         6
+#define GpioDriveH0D1         7
+
+#define GpioPinCnf_SENSE(x)   ((dword) x << 16)
+#define GpioSenseHigh         2
+#define GpioSenseLow          3
+
+#define nrfGpioDef
+#endif
+
+// Festlegungen für die Paketkonfigurationsregister
+//
+
+#define TwiInt_STOPPED    ((dword) 0x00000001 << 1)
+// Interrupt für Event STOPPED
+
+#define TwiInt_RXDREADY   ((dword) 0x00000001 << 2)
+// Interrupt für Event RXDREADY
+
+#define TwiInt_TXDSENT    ((dword) 0x00000001 << 7)
+// Interrupt für Event TXDSENT
+
+#define TwiInt_ERROR      ((dword) 0x00000001 << 9)
+// Interrupt für Event ERROR
+
+#define TwiInt_BB         ((dword) 0x00000001 << 14)
+// Interrupt für Event BB
+
+#define TwiInt_SUSPENDED  ((dword) 0x00000001 << 18)
+// Interrupt für Event SUSPENDED
+
+#define TwiEnable   5
+#define TwiDisable  0
+
+
+
+
+// ----------------------------------------------------------------------------
+
+class nRF52840Twi : IntrfTw
+{
+private:
+  // --------------------------------------------------------------------------
+  // Lokale Daten und Funktionen
+  // --------------------------------------------------------------------------
+  //
+  nrfTwiPtr     twiPtr;
+  dword         irqCounter;
+
+  TwiBytePtr    byteStruPtr;
+  TwiWordPtr    wordStruPtr;
+  TwiByteSeqPtr byteSeqPtr;
+
+  TwiTrfMode    trfMode;
+  dword         lastError;
+  byte          lastIn;
+  int           comIdx;
+
+  int           curIRQ;
+  dword         curIntEn;
+
+  TwiByte       tmpByte;
+
+  TwiParams     params;
+
+  void clrAllEvents();
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen der Basis-Klasse
+  // --------------------------------------------------------------------------
+
+  nRF52840Twi();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  TwiError begin(TwiParamsPtr inParPtr);
+  void getParams(TwiParamsPtr parPtr);
+
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+
+  // --------------------------------------------------------------------------
+  // Datenaustausch
+  // --------------------------------------------------------------------------
+  //
+  // asynchrone Kommunikation, Zustand in TwiByte.twiStatus
+  //
+  TwiError sendByte(int adr, TwiBytePtr refByte);
+  TwiError sendByteReg(int addr, int reg, TwiBytePtr refByte);
+  TwiError recByteReg(int addr, int reg, TwiBytePtr refByte);
+  TwiError recByteRegSeq(int adr, int reg, TwiByteSeqPtr refByteSeq);
+
+  // synchrone Kommunikation
+  //
+  TwiStatus writeByteReg(int adr, int reg, byte value);
+  int       readByteReg(int adr, int reg);
+  TwiStatus readByteRegSeq(int adr, int reg, TwiByteSeqPtr refByteSeq);
+
+  // ----------------------------------------------------------------------------
+  // Ereignisbearbeitung und Interrupts
+  // ----------------------------------------------------------------------------
+  //
+  static  nRF52840Twi *instPtr0;
+  static  void irqHandler0();
+
+  static  nRF52840Twi *instPtr1;
+  static  void irqHandler1();
+
+  void    irqHandler();
+
+  // ----------------------------------------------------------------------------
+  //                      D e b u g - H i l f e n
+  // ----------------------------------------------------------------------------
+  //
+  int           irqIdx;
+  int           irqList[8];
+
+  byte          extraValue;
+  bool          firstRead;
+
+  dword   getIrqCount();
+  void    resetIrqList();
+  void    getIrqList(char *dest);
+
+};
+
+#endif // NRF52840RADIO_H
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/BlePoll/BlePoll.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/BlePoll/BlePoll.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f2b89ae3d67d527cf753ec12a685ac02e0bde3ae
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/BlePoll/BlePoll.cpp
@@ -0,0 +1,1624 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   BlePoll.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   1. September 2021
+//
+// Diese Bibliothek (Klassse) enthält diverse Ressourcen zur Kommunikation
+// über BLE-Funkmodule auf niedriger Ebene, also dem direkten Telegrammaustausch.
+// Darauf aufbauend sollen mehrkanalige Messeinrichtungen mit möglichst
+// geringen Latenzzeiten entwickelt werden.
+//
+
+#include "BlePoll.h"
+
+// --------------------------------------------------------------------------
+// Textmakros zur Vereinfachung der Programmierung
+// --------------------------------------------------------------------------
+
+#define next(x) nextState = &BlePoll::x
+
+// --------------------------------------------------------------------------
+// Initialisierungen
+// --------------------------------------------------------------------------
+
+BlePoll::BlePoll(IntrfRadio *refRadio, dword inCycleMics)
+{
+  init(refRadio, inCycleMics, NULL);
+}
+
+BlePoll::BlePoll(IntrfRadio *refRadio, MicsecFuPtr inMicroFu)
+{
+  init(refRadio, 0, inMicroFu);
+}
+
+void BlePoll::init(IntrfRadio *refRadio, dword inCycleMics, MicsecFuPtr inMicroFu)
+{
+  radio = refRadio;
+  radio->setPacketParms(bptAdv);
+  radio->setAccessAddress(AdvAccAddr);
+  chn = adr = area = 0;
+  master = nak = eadr = false;
+  plMode = plmIdle;
+  plpType = plptEmpty;
+  micSec = inMicroFu;
+  cycleMics = inCycleMics;
+  cycleCnt = 0;
+  toValue = 0;
+  toSet = 0;
+  nextState = NULL;
+  slaveIdx = 0;
+  pollIdx = 0;
+  pollNr = 0;
+  maxAdr = MAXSLAVE;
+  curSlave = NULL;
+  cntAlien = 0;
+  cntWrong = 0;
+  cntAllNaks = 0;
+  cntWaitDisabled = 0;
+  cntPolling = 0;
+  cntAllRecs = 0;
+  cntAllTo = 0;
+  pollStopped = false;
+  pollStop = false;
+  recStopped = false;
+  recStop = false;
+  runCounter = 0;
+  newValue = false;
+  cbData = NULL;
+
+  for(int i = 1; i <= MAXSLAVE; i++)
+  {
+    slaveList[i].cntNakEP = 0;
+    slaveList[i].cntTo = 0;
+    slaveList[i].pIdx = 0;
+    slaveList[i].delayCnt = 5;
+    slaveList[i].newPdu = false;
+    slaveList[i].newMeas = false;
+    pollList[i].prioCnt = 0;
+    pollList[i].slIdx = 0;
+    pollList[i].status = 0;
+  }
+
+  DataExchange = false;
+}
+
+// ----------------------------------------------------------------------------
+void BlePoll::begin(ComType typeIn, int adrIn, AppType appType, dword watchDog)
+{
+  // TODO
+  // --------------------------------------------------------------------------
+  // Das muss nochmal völlig neu überarbeitet werden.
+  // Zur Zeit sind viele Redundanzen und teilweise Mehrdeutigkeiten enthalten,
+  // weil für jede Testanwendung spezifische Vorbereitungen gemacht wurden.
+  // --------------------------------------------------------------------------
+  //
+  wdTimeOut = watchDog;   // WatchDog-Time-Out in Mikrosekunden
+
+  if(typeIn == ctMASTER)
+    master  = true;
+  else
+    master = false;  void  resetPollCounters();
+
+
+  chn     = 0;          // 1. Bewerbungskanal
+  area    = 0;          // Default-Anwendungsbereich
+  eadr    = true;       // Start mit leerem Polling
+
+  // --------------------------------------------------------------------------
+  plMode  = plmEmpty;   // Leeres Polling (Adressenliste)
+  // --------------------------------------------------------------------------
+
+  if(master)
+  {
+    nak = true;         // Nak-Bit vom Master forciert leere Antwort
+    maxAdr = adrIn;
+    if(maxAdr > MAXSLAVE)
+      maxAdr = MAXSLAVE;
+    adr  = 1;
+    slaveIdx = adr;     // Reserve für getrennte Verwaltung von adr und slaveIdx
+    next(smInit);
+  }
+  else
+  {
+    nak = true;
+    adr = adrIn;
+    next(smInit);
+  }
+
+  if(appType == atSOAAP || appType == atTestSend || appType == atDevSOAAP)
+  {
+    pduOut.adr5 = 0x53;
+    pduOut.adr4 = 0x4F;
+    pduOut.adr3 = 0x41;
+
+    pduIn.adr5 = 0x53;
+    pduIn.adr4 = 0x4F;
+    pduIn.adr3 = 0x41;
+  }
+
+  if(appType == atTestSend)
+    plMode = plmTest;
+
+  pduOut.head = HeadS0B;
+  pduIn.head = HeadS0B;
+
+  pduOut.data[0] = 0;         // Pdu-Zähler (CNT)
+  pduIn.data[0] = 0;
+
+  pduOut.data[1] = appType;   // Pdu-Typ (TYPE)
+  pduIn.data[1] = appType;
+
+  if(appType == atSOAAP)
+  {
+    if(master)
+    {
+      valuePdu.appId = plptMeas9Ctrl4;
+      ctrlPdu.appId = plptCtrlX;
+      plMode = plmSoaapM;
+      fullCycle = true;
+    }
+    else
+    {
+      valuePdu.appId = plptMeas9Ctrl4;
+      ctrlPdu.appId = plptCtrlX;
+      plMode = plmSoaapS;
+    }
+  }
+  else if (appType == atDevSOAAP)
+  {
+    if(master)
+    {
+      valuePdu.appId = plptMeas13;
+      ctrlPdu.appId = plptCtrl2;
+      plMode = plmSoaapM;
+      fullCycle = true;
+    }
+    else
+    {
+      valuePdu.appId = plptMeas13;
+      ctrlPdu.appId = plptCtrl2;
+      plMode = plmSoaapS;
+    }
+  }
+
+  valuePdu.measCnt = 0;
+  valuePdu.counter = 0;
+  valuePdu.type = appType;
+}
+
+
+// --------------------------------------------------------------------------
+// Konfiguration
+// --------------------------------------------------------------------------
+//
+void BlePoll::setPollAddress(int chnIn, int adrIn, int areaIn, bool masterIn, bool eadrIn, bool nakIn)
+{
+  chn     = chnIn;
+  adr     = adrIn;
+  area    = areaIn;
+  master  = masterIn;
+  eadr    = eadrIn;
+  nak     = nakIn;
+}
+
+void BlePoll::setPduAddress()
+{
+  setPduAddress(&pduOut);
+}
+
+void BlePoll::setPduAddress(bcPduPtr pduPtr)
+{
+  pduPtr->adr0 = (byte) adr;
+  pduPtr->adr1 = (byte) (area & 0x3F);
+  if(nak) pduPtr->adr1 |= 0x40;
+  if(eadr) pduPtr->adr1 |= 0x80;
+  pduPtr->adr2 = (byte) chn;
+  if(master) pduPtr->adr2 |= 0x80;
+}
+
+void BlePoll::setEmptyPollParams(int cycleTotal, int cycleRun, dword timeOut)
+{
+  epCycleTotal  = cycleTotal;
+  epCycleRun    = cycleRun;
+  epTimeOut     = timeOut;
+}
+
+void BlePoll::setDataPollParams(int slAdr, int prio, int minPrio, dword timeOut)
+{
+  if(slAdr > MAXSLAVE) return;
+  slaveList[slAdr].prioSet = prio;
+  slaveList[slAdr].minPrio = minPrio;
+  slaveList[slAdr].timeOut = timeOut;
+}
+
+void BlePoll::setCbDataPtr(cbDataPtr cbPtr)
+{
+  cbData = cbPtr;
+}
+
+void BlePoll::setCbCtrlPtr(cbCtrlPtr cbPtr)
+{
+  cbCtrl = cbPtr;
+}
+
+
+dword smStartComESCnt;
+bcPdu recBeacon;
+int   lenBeacon = 0;
+
+// --------------------------------------------------------------------------
+// Hilfsfunktionen
+// --------------------------------------------------------------------------
+//
+void BlePoll::setTimeOut(dword value)
+{
+  if(micSec != NULL)
+  {
+    toSet = micSec();
+    toValue = value;
+  }
+  else
+  {
+    if(cycleMics > 1)
+      cycleCnt = value / cycleMics;
+    else
+      cycleCnt = value;
+  }
+}
+
+bool BlePoll::timeOut()
+{
+  if(micSec != NULL)
+  {
+    if((micSec() - toSet) > toValue)
+      return(true);
+    else
+      return(false);
+  }
+  else
+  {
+    if(cycleCnt > 0)
+      return(false);
+    else
+      return(true);
+  }
+}
+
+bool BlePoll::getValues(bcPduPtr pduPtr, PlpType appId)
+{
+  bool  retv = false;
+
+  switch (appId)
+  {
+    case plptMeas6:
+      pduPtr->len = sizeof(PlpMeas6) + 6;
+      break;
+
+    case plptMeas9Ctrl4:
+      pduPtr->len = sizeof(PlpM9C4) + 6;
+      break;
+  }
+  pduPtr->data[0]++;      // Pdu-Counter
+  pduPtr->data[1] = valuePdu.type;
+  pduPtr->data[2] = appId;
+
+  newValue = cbData(appId, &pduPtr->data[4]);
+
+  if(newValue)
+  {
+    retv = true;
+    pduPtr->data[3]++;              // measCnt
+  }
+  return(retv);
+}
+
+
+bool BlePoll::getCtrls(bcPduPtr pduPtr, PlpType appId)
+{
+  int   ctrlLen;
+
+  if(recBeacon.len > 6)
+    ctrlLen = recBeacon.len - 6;
+  else
+    ctrlLen = 4;
+
+  newCtrl = cbCtrl((PlpType) appId, &pduPtr->data[22], &recBeacon.data[0], ctrlLen);
+
+  return(newCtrl);
+}
+
+
+// --------------------------------------------------------------------------
+// Steuerung des Polling
+// --------------------------------------------------------------------------
+//
+
+// Aktuellen Betriebszustand einstellen
+//
+void BlePoll::start(PlMode inPlMode)
+{
+  oldPlMode = plMode;
+  plMode = inPlMode;
+  pollStop = true;
+}
+
+// Anhalten des leeren Polling
+//
+void BlePoll::stopEP()
+{
+  pollStop = true;
+}
+
+// Weiterlaufen des leeren Polling
+//
+void BlePoll::resumeEP()
+{
+  pollStop    = false;
+  pollStopped = false;
+}
+
+// Abfrage, ob gestoppt
+//
+bool BlePoll::stoppedEP()
+{
+  return(pollStopped);
+}
+
+// Anhalten des Empfangs beim Slave
+//
+void BlePoll::stopSR()
+{
+  recStop = true;
+}
+
+// Weiterlaufen des Empfangs beim Slave
+//
+void BlePoll::resumeSR()
+{
+  recStop     = false;
+  recStopped  = false;
+}
+
+// Abfrage, ob Slaveempfang gestoppt
+//
+bool BlePoll::stoppedSR()
+{
+  return(recStopped);
+}
+
+
+// Eintritt in die Zustandsmaschine
+//
+void BlePoll::run()
+{
+  runCounter++;
+  if(cycleCnt > 0) cycleCnt--;
+
+  if(nextState != NULL)
+    (this->*nextState)();
+}
+
+// --------------------------------------------------------------------------
+// Zugriff auf Polling-Informationen
+// --------------------------------------------------------------------------
+//
+int BlePoll::getSlaveList(byte *dest, int maxByte)
+{
+  int           slIdx;
+
+  for(int i = 1; i <= pollMaxNr; i++)
+  {
+    if(i == maxByte) break;
+    slIdx = pollList[i].slIdx;
+    dest[i-1] = slaveList[slIdx].adr;
+  }
+  return(pollMaxNr);
+}
+
+void  BlePoll::resetPollCounters()
+{
+  int           slIdx;
+  SlavePtr      slPtr;
+
+  for(int i = 1; i <= pollMaxNr; i++)
+  {
+    slIdx = pollList[i].slIdx;
+    slPtr = &slaveList[slIdx];
+    slPtr->cntAckDP = 0;
+    slPtr->cntErrCrc = 0;
+    slPtr->cntLostMeas = 0;
+    slPtr->cntLostPdu = 0;
+    slPtr->cntNakEP = 0;
+    slPtr->cntTo = 0;
+  }
+}
+
+
+
+
+// ****************************************************************************
+// Zustandsmaschine
+// ****************************************************************************
+//
+
+// ----------------------------------------------------------------------------
+// Verzweigung nach Anwendung (nach Anlauf)
+// ----------------------------------------------------------------------------
+//
+dword smInitCnt;
+
+void BlePoll::smInit()
+{
+  bleState = 100;
+  smInitCnt++;
+
+  switch(plMode)
+  {
+    case plmIdle:
+      break;
+
+    case plmTest:
+      next(smStartTest);
+      break;
+
+    case plmEmpty:
+      epCycleTotal = -1;
+      epCycleRun = -1;
+      next(smStartEP);
+      break;
+
+    case plmScan:
+      break;
+
+    case plmSoaapM:
+      if(pollStop)
+        pollStopped = true;
+      if(!pollStopped)
+        next(smStartEP);
+      break;
+
+    case plmSoaapS:
+      next(smStartComES);
+      break;
+
+    case plmXchg:
+      break;
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Verzweigung nach Anwendung (im Betrieb)
+// ----------------------------------------------------------------------------
+//
+dword smIdleCnt;
+
+void BlePoll::smIdle()
+{
+  bleState = 200;
+  smIdleCnt++;
+
+  switch(plMode)
+  {
+    case plmIdle:
+      break;
+
+    case plmTest:
+      next(smStartTest);
+      break;
+
+    case plmEmpty:
+      next(smStartEP);
+      break;
+
+    case plmScan:
+      if(master)
+        next(smReqComS);
+      break;
+
+    case plmXchg:
+      if(!master)
+        next(smStartComES);
+      break;
+
+    case plmSoaapM:
+      if(!pollStopped)
+        next(smStartEP);
+      break;
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Low Level Tests
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smStartTest()
+{
+  bleState = 500;
+
+  if(master)
+  {
+    nak = false;
+    adr = 1;
+    slaveIdx = adr;
+    setTimeOut(500000);
+  }
+  else
+  {
+    nak = true;
+  }
+  pduOut.len  = 6;
+  setPduAddress();
+  radio->setChannel(chn);
+  next(smWaitTest);
+}
+
+void BlePoll::smWaitTest()
+{
+  if(!timeOut()) return;
+  radio->disable(txmRead);
+  next(smLoopTest);
+}
+
+void BlePoll::smLoopTest()
+{
+  pduOut.adr0++;
+  radio->send(&pduOut, txmRead);
+  setTimeOut(500000);
+  next(smWaitTest);
+}
+
+
+
+// ----------------------------------------------------------------------------
+// L e e r e s   P o l l i n g
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smStartEP()
+{
+  bleState = 1000;
+
+  pduOut.len  = 6;          // Nur Adresse
+  radio->setChannel(chn);
+  pollMaxNr = 0;
+
+  nak = true;
+
+  if(master)
+  {
+    adr = 1;
+    slaveIdx = adr;         // Slave-Array[0] reserviert
+    pollIdx = 1;            // Poll-Array[0] reserviert
+    pollNr = 0;             // Anzahl in der Poll-Liste
+    next(smReqEadr);
+  }
+  else
+  {
+    next(smWaitEadr);
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Leeres Polling           M a s t e r
+// ----------------------------------------------------------------------------
+//
+
+void BlePoll::smReqEadr()
+{
+  bleState = 1100;
+
+  // Datenstruktur für den Slave definieren
+  //
+  curSlave = &slaveList[slaveIdx];    // Zeiger auf zu pollenden Slave
+  curSlave->adr  = adr;
+  curSlave->area = area;
+  curSlave->chn  = chn;
+
+  curPoll = &pollList[pollIdx];       // Zeiger auf freien Platz in Poll-Liste
+
+  setPduAddress();
+  setTimeOut(epTimeOut);
+
+  radio->getStatistics(&statistic);
+
+  radio->send(&pduOut, txmPoll);
+  cntPolling++;
+  next(smWaitNak);
+}
+
+
+void BlePoll::smWaitNak()
+{
+  bleState = 1110;
+
+  if(timeOut())
+  {
+    // Für die Adresse ist kein Teilnehmer vorhanden, oder die Übertragung ist gestört
+    //
+    radio->disable(txmPoll);
+
+    if(curSlave->pIdx != 0)
+    {
+      // Wenn der Teilnehmer bereits in die Poll-Liste eingetragen ist
+      // dann wird darin seine temporäre Abwesenheit markiert
+      pollList[(int) curSlave->pIdx].status &= ~psSlaveIsPresent;
+    }
+
+    // Der Time-Out-Zähler macht erkenntlich, wie stark sich Störungen auswirken
+    //
+    curSlave->cntTo++;
+    cntAllTo++;
+
+    // Nächste Adresse und zur Anforderung
+    //
+    adr++;
+    slaveIdx = adr;
+    if(adr > maxAdr)
+      next(smEndEP);
+    else
+      next(smReqEadr);
+    return;
+  }
+
+
+  if(radio->fin(txmPoll, &crcError))
+  {
+    // Auf dem Kanal wurde ein Datensatz (BLE-Beacon) empfangen
+    // und es werden die ersten 8 Byte (Header, Len und Adresse) geholt
+    //
+    cntAllRecs++;
+    radio->getRecData(&pduIn, 8);
+
+    if(pduIn.adr3 != pduOut.adr3 || pduIn.adr4 != pduOut.adr4 || pduIn.adr5 != pduOut.adr5)
+    {
+      // Wenn die höherwertigen 3 Byte der Adresse nicht mit der Anwendungskodierung
+      // übereinstimmen, dann ist es ein Fremd-Beacon.
+      // Diese werden gezählt und kennzeichnen, wie stark aktiv ein anderes
+      // BLE-Netzwerk in Reichweite ist.
+      //
+      cntAlien++;
+
+      // Nächste Adresse und zur Anforderung, nicht auf Time-Out warten
+      //
+      adr++;
+      slaveIdx = adr;
+      if(adr > maxAdr)
+        next(smEndEP);
+      else
+        next(smReqEadr);
+      return;
+    }
+
+    if(pduIn.adr0 != pduOut.adr0 || (pduIn.adr1 & 0x3F) != (pduOut.adr1 & 0x3F))
+    {
+      // Wenn die Teinehmernummer oder die Gebietsnummer falsch sind, dann ist
+      // möglicherweise ein weiterer Master aktiv, der fehlerhafterweise denselben
+      // Kanal verwendet.
+      // Auch dieses Ereignis wird gezäht.
+      cntWrong++;
+
+      // Nächste Adresse und zur Anforderung, nicht auf Time-Out warten
+      //
+      adr++;
+      slaveIdx = adr;
+      if(adr > maxAdr)
+        next(smEndEP);
+      else
+        next(smReqEadr);
+      return;
+    }
+
+    // Alles korrekt, der adressierte Teilnehmer hat rechtzeitig geantwortet
+    // Radio ist abgeschaltet
+    //
+
+    if(curSlave->pIdx != 0)
+    {
+      // Wenn der Teilnehmer bereits in die Poll-Liste eingetragen ist,
+      // dann wird er darin als aktuell anwesend markiert
+      //
+      pollList[(int) curSlave->pIdx].status |= psSlaveIsPresent;
+      pollNr++;
+    }
+    else
+    {
+      // Wenn nicht, wird der aktuelle Listeneintrag definiert
+      //
+      curPoll->status |= psSlaveIsPresent | psSlaveWasPresent;
+      curPoll->slIdx = slaveIdx;  // Querverweis zur Liste möglicher Teilnehmer
+      curSlave->pIdx = pollIdx;   // Und in der Liste auch ein Verweis zur Poll-Liste
+      pollIdx++;                  // Weiter mit nächstem Listenplatz
+      pollNr++;                   // Anzahl der beantworteten Pollings
+    }
+
+    // Die Nak-Antworten werden gezählt
+    //
+    curSlave->cntNakEP++; // Teilnehmerspezifisch
+    cntAllNaks++;         // und insgesamt
+
+    // Weiter mit nächstem Teilnehmer und nächster Adresse (TN-Nummer)
+    //
+    adr++;
+    slaveIdx = adr;
+
+    // Wenn die vorgegeben Endadresse erreicht ist
+    // dann zum Ende des Polldurchgangs über alle möglichen Teilnehmer,
+    // ansonsten nächsten Teilnehmer pollen
+    //
+    if(adr > maxAdr)
+      next(smEndEP);
+    else
+      next(smReqEadr);
+  }
+}
+
+
+void BlePoll::smEndEP()
+{
+  // Nach jedem vollständigen Polldurchlauf kann das Polling
+  // abgebrochen werden.
+  //
+  if(pollStop || pollStopped)
+  {
+    pollStopped = true;
+    next(smIdle);
+    return;
+  }
+
+  // Es werden die Maximalwerte der rechtzeitigen Antworten gebildet
+  //
+  if(pollNr > pollMaxNr)
+    pollMaxNr = pollNr;
+
+  if(pollMaxNr == 0)
+  {
+    // Wenn noch kein Teilnehmer angeschlossen ist (oder Kanal total gestört)
+    //
+    if(epCycleTotal > 0)
+    {
+      // dann wird der Pollvorgang so oft wie vorgegeben wiederholt
+      //
+      epCycleTotal--;
+      pollNr = 0;
+      pollIdx = 1;
+      adr = 1;
+      slaveIdx = adr;
+      next(smReqEadr);
+      return;
+    }
+    else if(epCycleTotal == 0)
+    {
+      // und anschließend der Idle-Zustand angenommen
+      next(smIdle);
+      return;
+    }
+  }
+  else
+  {
+    // Wenn wenigstens schon ein Teilnehmer geantwortet hat
+    //
+    if(epCycleRun > 0)
+    {
+      // dann wird der Pollvorgang auch so oft wie anderweitig vorgegeben wiederholt
+      //
+      epCycleRun--;
+      pollNr = 0;
+      //pollIdx = 1; falsch, pollIdx zeigt auf nächsten freien Platz
+      adr = 1;
+      slaveIdx = adr;
+      next(smReqEadr);
+      return;
+    }
+    else if(epCycleRun == 0)
+    {
+      // und anschließend die Datenübertragung gestartet
+      // oder das Polling ganz beendet
+      //
+      if(fullCycle)
+        next(smStartCom);
+      else
+        next(smIdle);
+      return;
+    }
+  }
+
+  // Nächster Poll-Lauf, wenn epCycleXXX noch nicht abgelaufen ist
+  // oder auf einen wert kleiner 0 gestellt wurde
+  //
+  pollNr = 0;
+  //pollIdx = 1; falsch, pollIdx zeigt auf nächsten freien Platz
+  adr = 1;
+  slaveIdx = adr;
+  next(smReqEadr);
+}
+
+// ----------------------------------------------------------------------------
+// Leeres Polling           S l a v e
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smWaitEadr()
+{
+  bleState = 1200;
+
+  if(!radio->disabled(txmRespE))
+  {
+    radio->disable(txmRespE);
+    cntWaitDisabled++;
+    return;
+  }
+
+  setPduAddress();
+  radio->send(&pduOut, txmRespE);
+  next(smEvalPoll);
+}
+
+void BlePoll::smEvalPoll()
+{
+  bleState = 1210;
+
+  radio->getStatistics(&statistic);
+  if(!radio->fin(txmRespE, &crcError)) return;
+  next(smWaitEadr);
+}
+
+// ----------------------------------------------------------------------------
+// D a t e n ü b e r t r a g u n g
+// ----------------------------------------------------------------------------
+//
+
+// Vorbereitung der Datenübertragung
+//
+void BlePoll::smStartCom()
+{
+  bleState = 2000;
+
+  if(pollStop || pollStopped) // Der Start der Datenübertragung kann
+  {                           // verzögert werden
+    pollStopped = true;
+    return;
+  }
+
+  // --------------------------------------------
+  // Auswahl des Funkkanals (Frequenz)
+  // --------------------------------------------
+  //
+  radio->setChannel(chn);
+
+  // --------------------------------------------
+  // Voreinstellungen für den Master
+  // --------------------------------------------
+  //
+  if(master)
+  {
+    // Aufbau der Polling-Liste
+    //
+    for(int i = 1; i <= pollMaxNr; i++)
+    {
+      int slIdx = pollList[i].slIdx;
+      pollList[i].prioCnt = slaveList[slIdx].prioSet;
+    }
+    pollIdx = 1;
+
+    // Vorbereitung der mit dem Polling übermittelten Daten
+    //
+    pduOut.len  = 13;                 // Adresse (6) + 7 Byte Steuerung
+    ctrlPdu.counter = 0;              // Zähler im Steuertelegramm
+    ctrlPdu.appId = plptMeas9Ctrl4;   // Info für Slave zur Antwort
+
+    next(smReqComS);
+  }
+  // --------------------------------------------
+  // Voreinstellungen für den Slave
+  // --------------------------------------------
+  //
+  else
+  {
+    nak = true;
+    next(smWaitEadr);
+  }
+
+  DataExchange = true;
+}
+
+// ----------------------------------------------------------------------------
+// Datenübertragung Master          S l a v e  - >  M a s t e r
+// ----------------------------------------------------------------------------
+//
+
+// M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M
+// Polling : Anfordern von Daten beim Slave
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smReqComS()
+{
+  bleState = 2100;
+
+  if(pollStop || pollStopped) // Das Polling kann
+  {                           // angehalten werden
+    pollStopped = true;
+    return;
+  }
+
+  // Es ist von einem vorherigen Funkempfang auszugehen
+  // Die Hardware muss erst ausgeschaltet werden
+  //
+  if(!radio->disabled(txmPoll))
+  {
+    radio->disable(txmPoll);
+    return;
+  }
+
+  // Der aufzufordernde Teilnehmer wird der Poll-Liste entnommen
+  //
+  curPoll = &pollList[pollIdx];
+
+  // Ein Aufruf erfolgt nur, wenn der Prioritätszähler auf 0 steht
+  // ansonsten wird er dekrementiert und der nächste Teilnehmer
+  // im nächsten Zustandslauf ausgewählt.
+  //
+  if(curPoll->prioCnt > 0)
+  {
+    curPoll->prioCnt--;
+    pollIdx++;
+    if(pollIdx > pollMaxNr)
+      pollIdx = 1;
+    return;
+  }
+
+  // Zugriff auf den Slave aus der Poll-Liste vorbereiten
+  //
+  slaveIdx = curPoll->slIdx;
+  curSlave = &slaveList[slaveIdx];
+
+  // Slave-spezifische Parameter setzen
+  //
+  eadr = false;         // Ist true, wenn Daten nur zum Slave übertragen werden
+  nak = false;          // Ist true, wenn keine Daten übertragen werden (empty poll)
+  adr = curSlave->adr;
+  area = curSlave->area;
+  setPduAddress();
+  setTimeOut(curSlave->timeOut);
+
+
+  // Statistic-Daten einholen für evt. Auswertung
+  //
+  radio->getStatistics(&statistic);
+
+  // Aufruf des Slave starten
+  //
+  radio->send(&pduOut, txmPoll);
+
+  cntPolling++;
+  next(smWaitAckComS);
+}
+
+// M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M
+// Warten auf die Antwort vom Slave
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smWaitAckComS()
+{
+  byte    tmpByte;
+  short   tmpShort;
+
+  // Zeiger zur spezifischen Betrachtung von Empfangsdaten
+  PlPduMeasPtr resPtr;
+
+  bleState = 2110;
+
+  if(timeOut())
+  {
+    // Wenn der Slave nicht antwortet (kann auch eine Störung sein),
+    // dann wird seine Priorität heruntergesetzt (Zählwert erhöht)
+    // und der nächste Slave aus der Poll-Liste angefragt
+    //  byte      oldPduCount;
+
+    curSlave->prioSet++;
+    if(curSlave->prioSet > curSlave->minPrio)
+      curSlave->prioSet = curSlave->minPrio;
+
+    curPoll->prioCnt = curSlave->prioSet;
+
+    curSlave->cntTo++;
+    pollIdx++;
+    if(pollIdx > pollMaxNr)
+      pollIdx = 1;
+
+    radio->disable(txmPoll);
+    next(smReqComS);
+    return;
+  }
+
+  if(radio->fin(txmPoll, &crcError))
+  {
+    cntAllRecs++;
+
+    // Wenn (irgend-) ein Beacon eingegangen ist,
+    // wird die maximale (BLE-Standard) Anzahl von Bytes kopiert
+    //
+    radio->getRecData(&pduIn, 39);
+
+    if(pduIn.adr3 != pduOut.adr3 || pduIn.adr4 != pduOut.adr4 || pduIn.adr5 != pduOut.adr5)
+    {
+      // Beacons aus fremdem Netzen werden nur gezählt und es wird weiter gewartet
+      //
+      cntAlien++;
+      pollIdx++;
+      if(pollIdx > pollMaxNr)
+        pollIdx = 1;
+
+      next(smReqComS);
+      return;
+    }
+
+    if(pduIn.adr0 != pduOut.adr0 || (pduIn.adr1 & 0x3F) != (pduOut.adr1 & 0x3F))
+    {
+      // Beacons mit falscher Slaveadresse werden ebenfalls nur gezählt
+      // Hier wird später die Rundrufübertragung implementiert
+      //
+      cntWrong++;
+      pollIdx++;
+      if(pollIdx > pollMaxNr)
+        pollIdx = 1;
+
+      next(smReqComS);
+      return;
+    }
+
+    // Antwort vom richtigen Teilnehmer ist eingegangen
+    //
+
+    if(crcError)
+    {
+      // Die Daten werden bei einem CRC-Fehler verworfen.
+      // Der Fehler wird gezählt und ist ein Hinweis auf fremde
+      // Funkaktivitäten
+      //
+      curSlave->cntErrCrc++;
+      pollIdx++;
+      if(pollIdx > pollMaxNr)
+        pollIdx = 1;
+
+      next(smReqComS);
+      return;
+    }
+
+    // Die Daten werden in der Slave-Struktur abgelegt
+    //
+    resPtr = (PlPduMeasPtr) &curSlave->result;
+
+    resPtr->counter  = pduIn.data[0];
+    resPtr->type     = pduIn.data[1];
+    resPtr->appId    = pduIn.data[2];
+    resPtr->measCnt  = pduIn.data[3];
+
+    // Die Inhalte sind abhängig von der <appId>
+    //
+    switch(resPtr->appId)
+    {
+      case plptMeas9Ctrl4:
+        ((PlpM9C4Ptr) resPtr)->meas[0]  = *(word *) &pduIn.data[4];
+        ((PlpM9C4Ptr) resPtr)->meas[1]  = *(word *) &pduIn.data[6];
+        ((PlpM9C4Ptr) resPtr)->meas[2]  = *(word *) &pduIn.data[8];
+        ((PlpM9C4Ptr) resPtr)->meas[3]  = *(word *) &pduIn.data[10];
+        ((PlpM9C4Ptr) resPtr)->meas[4]  = *(word *) &pduIn.data[12];
+        ((PlpM9C4Ptr) resPtr)->meas[5]  = *(word *) &pduIn.data[14];
+        ((PlpM9C4Ptr) resPtr)->meas[6]  = *(word *) &pduIn.data[16];
+        ((PlpM9C4Ptr) resPtr)->meas[7]  = *(word *) &pduIn.data[18];
+        ((PlpM9C4Ptr) resPtr)->meas[8]  = *(word *) &pduIn.data[20];
+        ((PlpM9C4Ptr) resPtr)->ctrlPath = pduIn.data[22];
+        ((PlpM9C4Ptr) resPtr)->procCnt  = pduIn.data[23];
+        ((PlpM9C4Ptr) resPtr)->ctrl[0]  = pduIn.data[24];
+        ((PlpM9C4Ptr) resPtr)->ctrl[1]  = pduIn.data[25];
+        break;
+
+      case plptMeas6:
+        ((PlpM9C4Ptr) resPtr)->meas[0]  = *(word *) &pduIn.data[4];
+        ((PlpM9C4Ptr) resPtr)->meas[1]  = *(word *) &pduIn.data[6];
+        ((PlpM9C4Ptr) resPtr)->meas[2]  = *(word *) &pduIn.data[8];
+        ((PlpM9C4Ptr) resPtr)->meas[3]  = *(word *) &pduIn.data[10];
+        ((PlpM9C4Ptr) resPtr)->meas[4]  = *(word *) &pduIn.data[12];
+        ((PlpM9C4Ptr) resPtr)->meas[5]  = *(word *) &pduIn.data[14];
+        ((PlpM9C4Ptr) resPtr)->meas[6]  = *(word *) &pduIn.data[16];
+        break;
+    }
+
+
+    // Zählen der verlorenen Telegramme und Messwerte
+    // beginnt um <delayCnt> Pollzyklen verzögert
+    //
+    if(curSlave->delayCnt == 0)
+    {
+      tmpByte = curSlave->result.counter - curSlave->oldPduCount;
+      if(tmpByte > 1)
+        curSlave->cntLostPdu += tmpByte - 1;
+
+      tmpByte = resPtr->measCnt - curSlave->oldMeasCount;
+      if(tmpByte != 0)
+        curSlave->newMeas = true;
+      if(tmpByte > 1)
+        curSlave->cntLostMeas += tmpByte - 1;
+    }
+    else curSlave->delayCnt--;
+
+    curSlave->oldPduCount = curSlave->result.counter;
+    curSlave->oldMeasCount = resPtr->measCnt;
+
+    curSlave->newPdu  = true;
+    curSlave->cntAckDP++;
+    curPoll->prioCnt = curSlave->prioSet;
+
+    pollIdx++;
+    if(pollIdx > pollMaxNr)
+      pollIdx = 1;
+
+    next(smReqComS);
+    return;
+  }
+}
+
+void BlePoll::smEndComS()
+{
+  if(pollStop || pollStopped)
+  {
+    pollStopped = true;
+    return;
+  }
+  // Von vorne (zur Zeit, Test)
+  //
+  adr = 1;
+  slaveIdx = adr;
+  next(smReqEadr);
+}
+
+// ----------------------------------------------------------------------------
+// Datenübertragung Master         M a s t e r  - >  S l a v e
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smReqComE()
+{
+  bleState = 4100;
+
+  if(!radio->disabled(txmRead))
+  {
+    radio->disable(txmRead);
+    return;
+  }
+
+  curSlave = &slaveList[slaveIdx];
+  curSlave->adr  = adr;
+  curSlave->area = area;
+  curSlave->chn  = chn;
+
+  setPduAddress();
+  //setTimeOut(2000);
+  // Test
+  setTimeOut(1000000);
+  radio->send(&pduOut, txmRead);
+  radio->getStatistics(&statistic);
+  cntPolling++;
+  next(smWaitNak);
+}
+
+void BlePoll::smWaitAckComE()
+{
+  bleState = 4110;
+
+  if(timeOut())
+  {
+    radio->disable(txmRead);
+    curSlave->cntTo++;
+    adr++;
+    slaveIdx = adr;
+    if(adr > maxAdr)
+      next(smEndEP);
+    else
+      next(smReqEadr);
+    return;
+  }
+
+
+  if(radio->fin(txmRead, &crcError))
+  {
+    radio->getRecData(&pduIn, 8);
+
+    if(pduIn.adr3 != pduOut.adr3 || pduIn.adr4 != pduOut.adr4 || pduIn.adr5 != pduOut.adr5)
+    {
+      cntAlien++;
+      radio->cont(txmRead);
+      return;
+    }
+
+    if(pduIn.adr0 != pduOut.adr0 || (pduIn.adr1 & 0x3F) != (pduOut.adr1 & 0x3F))
+    {
+      cntWrong++;
+      radio->cont(txmRead);
+      return;
+    }
+
+    radio->disable(txmRead);
+    curSlave->cntNakEP++;
+    cntAllNaks++;
+    adr++;
+    slaveIdx = adr;
+    if(adr > maxAdr)
+      next(smEndEP);
+    else
+      next(smReqEadr);
+  }
+  radio->getStatistics(&statistic);
+}
+
+void BlePoll::smEndComE()
+{
+  if(pollStop || pollStopped)
+  {
+    pollStopped = true;
+    return;
+  }
+  // Von vorne (zur Zeit, Test)
+  //
+  adr = 1;
+  slaveIdx = adr;
+  next(smReqEadr);
+}
+
+
+// S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S
+// ----------------------------------------------------------------------------
+// Datenübertragung Slave         M a s t e r  < - >  S l a v e
+// ----------------------------------------------------------------------------
+//
+
+// S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S
+// Vorbereitungen für den Empfang (Polling durch Master)
+// ----------------------------------------------------------------------------
+//
+void BlePoll::smStartComES()
+{
+  bool  newValues;
+  bool  newCtrl;
+  byte  lenValues;
+  byte  appId;
+
+  bleState = 1310;
+  smStartComESCnt++;
+
+  if(recStop || recStopped)
+  {
+    recStopped = true;
+    return;
+  }
+
+  // Wenn keine Daten von der Anwendung zur Verfügung gestellt werden,
+  // dann macht der Betrieb hier keinen Sinn und der Slave geht in IDLE
+  //
+  if(cbData == NULL)
+  {
+    next(smIdle);
+    return;
+  }
+
+  // Falls der Sender noch nicht ausgeschaltet ist, muss gewartet werden
+  //
+  if(!radio->disabled(txmResp))
+  {
+    radio->disable(txmResp);
+    cntWaitDisabled++;
+    return;
+  }
+
+  // Vorbereiten des erwarteten Inhalts beim Polling durch den Master
+  //
+  nak = true;
+  eadr = true;
+  setPduAddress(&pduIn);
+  pduIn.len = 6;
+
+
+  // Vorbereiten des zu sendenden Inhalts als Antwort auf das Polling
+  //
+  nak = false;
+  eadr = false;
+  setPduAddress(&pduOut);
+
+  // Eintragen der Messwerte in das Sendetelegramm
+  //
+  if(lenBeacon == 0)            // Wenn noch kein Empfangszyklus vorliegt
+    appId = valuePdu.appId;     // dann wird der voreingestellte Satz gewählt
+  else
+    appId = recBeacon.data[2];  // ansonsten der speziell angeforderte
+
+  newValues = getValues(&pduOut, (PlpType) appId);
+
+  if((appId == plptMeas9Ctrl4) && (cbCtrl != NULL))
+    getCtrls(&pduOut, (PlpType) appId);
+
+  radio->setChannel(chn);
+  radio->send(&pduIn, &pduOut, txmResp, newValues);
+
+  setTimeOut(wdTimeOut);
+  next(smWaitComES);
+}
+
+// S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S
+void BlePoll::smWaitComES()
+{
+  bleState = 1320;
+
+  radio->getStatistics(&statistic);
+  if(timeOut())
+  {
+    next(smStartComES);
+    return;
+  }
+
+  if(!radio->fin(txmResp, &crcError)) return;
+  //
+  // Übertragung beendet, Daten empfangen (polling) und versendet (response)
+  //
+  //lenBeacon = radio->getRecData(&recBeacon, txmResp, sizeof(recBeacon));
+  next(smStartComES);
+}
+
+// --------------------------------------------------------------------------
+// Anwenderfunktionen
+// --------------------------------------------------------------------------
+//
+
+// neue Steuerungsdaten für einen Slave
+
+void BlePoll::updControl(int adr, byte *ctrlList, int nr)
+{
+  if(adr <= 0) return;
+  if(adr >= MAXSLAVE) return;
+  if(nr <= 1) return;
+  if(nr > 27) return;
+
+  SlavePtr  slavePtr = &slaveList[adr];
+  PlpCtrl27Ptr ctrlPtr = (PlpCtrl27Ptr) &slavePtr->control;
+  for(int i = 0; i < nr; i++)
+    ctrlPtr->ctrl[i] = ctrlList[i];
+  ctrlPtr->ctrlCnt++;
+  slavePtr->rspOk = false;
+}
+
+// Feststellenn, ob Übertragung der Steuerungsdaten erfolgt ist
+
+bool BlePoll::ackTrans(int adr)
+{
+  if(adr <= 0) return(false);
+  if(adr >= MAXSLAVE) return(false);
+
+  SlavePtr  slavePtr = &slaveList[adr];
+  return(slavePtr->rspOk);
+}
+
+// Feststellen, ob Steuerungsdaten beim Slave verarbeitet sind
+
+bool BlePoll::ackControl(int adr)
+{
+  if(adr <= 0) return(false);
+  if(adr >= MAXSLAVE) return(false);
+
+  SlavePtr  slavePtr = &slaveList[adr];
+  PlpCtrl27Ptr ctrlPtr = (PlpCtrl27Ptr) &slavePtr->control;
+  if(ctrlPtr->ctrlCnt == slavePtr->rspCtrlCount)
+    return(true);
+  else
+    return(false);
+}
+
+// ----------------------------------------------------------------------------
+// Zugriff auf Slavedaten über die Adresse
+// ----------------------------------------------------------------------------
+//
+
+// Feststellen, ob ein Slave neue Messwerte hat
+//
+bool  BlePoll::measAvail(int slAdr)
+{
+  if(slAdr < 1) return(false);
+  if(slAdr >= MAXSLAVE) return(false);
+
+  SlavePtr  slavePtr = &slaveList[slAdr];
+
+  if(!slavePtr->newMeas)
+    return(false);
+
+  slavePtr->newMeas = false;
+  return(true);
+}
+
+// Auslesen der Netzwerk-Area
+//
+int BlePoll::getArea(int slAdr)
+{
+  if(slAdr < 1) return(false);
+  if(slAdr >= MAXSLAVE) return(false);
+
+  SlavePtr  slavePtr = &slaveList[slAdr];
+
+  return(slavePtr->area);
+}
+
+// Auslesen der AppId aus Sicht der Klasse BlePoll
+//
+PlpType BlePoll::getAppId(int slAdr)
+{
+  if(slAdr < 1) return(plptError);
+  if(slAdr >= MAXSLAVE) return(plptError);
+
+  SlavePtr  slavePtr = &slaveList[slAdr];
+
+  return((PlpType) slavePtr->result.plData[0]);
+}
+
+// Auslesen der Messwerte
+//
+int BlePoll::getMeas(int slAdr, byte *dest)
+{
+  int     anzByte;
+  PlpType appId;
+
+  if(slAdr < 1) return(false);
+  if(slAdr >= MAXSLAVE) return(false);
+
+  SlavePtr  slavePtr = &slaveList[slAdr];
+
+  appId = (PlpType) slavePtr->result.plData[0];
+
+  switch(appId)
+  {
+    case plptMeas6:
+      anzByte = 12;
+      break;
+
+    case plptMeas9:
+      anzByte = 18;
+      break;
+
+    case plptMeas9Ctrl4:
+      anzByte = 22;
+      break;
+
+    case plptMeas13:
+      anzByte = 26;
+      break;
+
+    default:
+      anzByte = 18;
+      break;
+  }
+
+  for (int i = 0; i < anzByte; i++)
+  {
+    dest[i] = slavePtr->result.plData[i+2];
+  }
+
+  return(anzByte);
+}
+
+int BlePoll::getCtrlM(int slAdr, byte *dest){
+  int     anzByte;
+  PlpType appId;
+
+  if(slAdr < 1) return(false);
+  if(slAdr >= MAXSLAVE) return(false);
+
+  
+  SlavePtr  slavePtr = &slaveList[slAdr];
+  appId = (PlpType) slavePtr->result.plData[0];
+
+  switch(appId)
+  {
+    case plptMeas6:
+      anzByte = 0;
+      break;
+
+    case plptMeas9:
+      anzByte = 0;
+      break;
+
+    case plptMeas9Ctrl4:
+      anzByte = 4;
+      break;
+
+    case plptMeas13:
+      anzByte = 0;
+      break;
+
+    default:
+      anzByte = 0;
+      break;
+  }
+
+  for (int i = 0; i < anzByte; i++)
+  {
+    dest[i] = slavePtr->result.plData[i+20];
+  }
+
+  return(anzByte);
+}
+
+
+// --------------------------------------------------------------------------
+// Debugging
+// --------------------------------------------------------------------------
+//
+dword BlePoll::debGetDword(int idx)
+{
+  dword retv = 0;
+
+  switch(idx)
+  {
+    case 0:
+      retv = plMode;
+      break;
+
+    case 1:
+      retv = cntAllRecs;
+      break;
+
+    case 2:
+      retv = cntAllNaks;
+      break;
+
+    case 3:
+      retv = cntAllTo;
+      break;
+
+    case 4:
+      retv = cntAlien;
+      break;
+
+    case 5:
+      retv = cntWrong;
+      break;
+
+    case 6:
+      retv = statistic.pollAcks;
+      break;
+
+    case 7:
+      retv = statistic.pollNaks;
+      break;
+
+    case 8:
+      retv = bleState;
+      break;
+
+    case 9:
+      retv = radio->getState();
+      break;
+  }
+
+  return(retv);
+}
+
+dword BlePoll::getStatistics(TxStatisticsPtr dest)
+{
+  *dest = statistic;
+  return(bleState);
+}
+
+
+SlavePtr      BlePoll::getSlavePtr(int idx)
+{
+  return(&slaveList[idx]);
+}
+
+PollStatePtr  BlePoll::getPollPtr(int idx)
+{
+  return(&pollList[idx]);
+}
+
+
+
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/BlePoll/BlePoll.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/BlePoll/BlePoll.h
new file mode 100644
index 0000000000000000000000000000000000000000..721d22c5c1b2fd65fc1415d7707fb2d93383e036
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/BlePoll/BlePoll.h
@@ -0,0 +1,513 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   BlePoll.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   1. September 2021
+//
+// Diese Bibliothek (Klassse) enthält diverse Ressourcen zur Kommunikation
+// über BLE-Funkmodule auf niedriger Ebene, also dem direkten Telegrammaustausch.
+// Darauf aufbauend sollen mehrkanalige Messeinrichtungen mit möglichst
+// geringen Latenzzeiten entwickelt werden.
+//
+
+#ifndef BlePoll_h
+#define BlePoll_h
+// ----------------------------------------------------------------------------
+
+#include "stddef.h"
+#include "string.h"
+#include "arduinoDefs.h"
+#include "bleSpec.h"
+#include "IntrfRadio.h"
+
+#define MAXSLAVE  100
+
+// Betriebsmodus (Polling und Slave)
+//
+typedef enum _PlMode
+{
+  plmIdle,        // Ruhezustand, Gerät nicht im Netz aktiv
+  plmTest,        // Low Level Tests
+  plmEmpty,       // Leeres Polling (Aufbau Adressliste)
+  plmScan,        // Daten aller aktiven Teilnehmer holen (Master)
+  plmSoaapM,      // Vollständiger Betrieb SOAAP (Master)
+  plmSoaapS,      // Vollständiger Betrieb SOAAP (Slave)
+  plmXchg         // Daten übertragen (Slave, beide Richtungen)
+} PlMode;
+
+// Grundsätzliche Datenstruktur für die Nutzdaten (ohne 6 Bytes Adresse)
+//
+typedef struct _PlPduBase   // maximale Länge Beacon = 31 Bytes
+{
+  byte  counter;      // zyklischer Telegrammmzähler
+  byte  type;         // Kennzeichnung der Datenstruktur (AppType)
+  byte  plData[29];   // weitere spezifische Nutzdaten
+} PlPduBase, *PlPduBasePtr;
+
+// Grundsätzliche Datenstruktur für Messwerte (ohne 6 Bytes Adresse)
+//
+typedef struct _PlPduMeas   // maximale Länge Beacon = 31 Bytes
+{
+  byte  counter;      // zyklischer Telegrammmzähler
+  byte  type;         // Kennzeichnung der Datenstruktur (AppType)
+  byte  appId;        // Kennzeichnung für Dateninhalte (PlpType)
+  byte  measCnt;      // Zähler für Messwertaktualisierung
+  byte  plData[27];   // weitere spezifische Nutzdaten
+} PlPduMeas, *PlPduMeasPtr;
+
+// Erweiterte Datenstruktur für die Nutzdaten (ohne 6 Bytes Adresse)
+// zur Zeit noch nicht genutzt
+//
+typedef struct _PlPduExtd   // grundsätzliche maximale Länge = 249 Bytes
+{
+  byte  counter;      // zyklischer Telegrammmzähler
+  byte  type;         // Kennzeichnung der Datenstruktur (AppType)
+  byte  plData[247];  // weitere spezifische Nutzdaten
+} PlPduExtd, *PlPduExtdPtr;
+
+// Datentypen (appId in plPduBase)
+//
+typedef enum _PlpType
+{
+  plptError,        // Fehler erkannt
+  plptEmpty,        // Leeres Telegramm (nur adresse)
+  plptBasic,        // Grundlegende Bytestruktur
+  plptFullMeas,     // Maximale Belegung mit 16-Bit Messwerten (word)
+  plptMeas3,        // 3 Messwerte (1 Raumsensor)
+  plptMeas6,        // 6 Messwerte (2 Raumsensoren)
+  plptMeas9,        // 9 Messwerte (3 Raumsensoren)
+  plptMeas9Ctrl4,   // 9 Messwerte + 4 Byte Steuerung
+  plptMeas10Ctrl4,  // 10 Messwerte + 4 Byte Steuerung
+  plptMeas12,       // 12 Messwerte (9 + 6 Byte Extradaten)
+  plptMeas13,       // 13 Messwerte (9 + 8 Byte Extradaten)
+  plptMeasX,        // Variable Anzahl Messwerte
+  plptCtrl0,        // Keine Steuerung
+  plptCtrl2,        // 2 Byte Steuerung
+  plptCtrl27,       // 27 Byte Steuerung
+  plptCtrlX,        // Variable Anzahl Steuerbytes
+  plptMsg           // (Quittierte) Meldung an Slave
+} PlpType, *PlpTypePtr;
+
+// Spezifische Datenstrukturen
+//
+typedef struct _PlpFullMeas   // Ausnahme für vordefinierte Spezialfälle
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  word    meas[14];   // Liste von 14 Messwerten
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    align;      // Wird nicht gesendet, kennzeichnet Alignement
+} PlpFullMeas, *PlpFullMeasPtr;
+
+typedef struct _PlpMeas3      // Länge 10 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    measCnt;    // Zähler für Messwertaktualisierung
+  word    meas[3];    // Liste von 3 Messwerten
+} PlpMeas3, *PlpMeas3Ptr;
+
+typedef struct _PlpMeas6      // Länge 16 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    measCnt;    // Zähler für Messwertaktualisierung
+  word    meas[6];    // Liste von 6 Messwerten
+} PlpMeas6, *PlpMeas6Ptr;
+
+typedef struct _PlpMeas9      // Länge 22 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    measCnt;    // Zähler für Messwertaktualisierung
+  word    meas[9];    // Liste von 9 Messwerten
+} PlpMeas9, *PlpMeas9Ptr;
+
+typedef struct _PlpM9C4      // Länge 26 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    measCnt;    // Zähler für Messwertaktualisierung
+  word    meas[9];    // Liste von 9 Messwerten
+  byte    ctrlPath;   // Steuerungspfad (für Verzweigungen/Funktionen)
+  byte    procCnt;    // Prozesszähler
+  byte    ctrl[2];    // Steuerungsdaten
+} PlpM9C4, *PlpM9C4Ptr;
+
+typedef struct _PlpMeas12      // Länge 28 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    measCnt;    // Zähler für Messwertaktualisierung
+  word    meas[12];   // Liste von 12 Messwerten
+} PlpMeas12, *PlpMeas12Ptr;
+
+typedef struct _PlpMeas13      // Länge 30 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    measCnt;    // Zähler für Messwertaktualisierung
+  word    meas[13];   // Liste von 13 Messwerten
+} PlpMeas13, *PlpMeas13Ptr;
+
+typedef struct _PlpCtrl2        // Länge 7 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    ctrlCnt;    // Zähler für Kommandoaktualisierung
+  byte    procCnt;    // Zähler für Prozessaktualisierung
+  byte    ctrl[2];    // Liste von 2 Steuerbytes
+} PlpCtrl2, *PlpCtrl2Ptr;
+
+typedef struct _PlpCtrl27        // Länge 31 (+ 6 Bytes Adresse)
+{
+  byte    counter;    // zyklischer Telegrammmzähler
+  byte    type;       // Kennzeichnung der Datenstruktur (AppType)
+  byte    appId;      // Kennzeichnung für Dateninhalte (PlpType)
+  byte    ctrlCnt;    // Zähler für Kommandoaktualisierung
+  byte    procCnt;    // Zähler für Prozessaktualisierung
+  byte    ctrl[26];   // Liste von bis zu 26 Steuerbytes
+} PlpCtrl27, *PlpCtrl27Ptr;
+
+// Identifikator für die Art der Daten
+//
+typedef enum _MeasId
+{
+  app               // Gestaltung/Bedeutung der Daten aus Anwendung
+} MeasId, *MeasIdPtr;
+
+typedef struct _Slave
+{
+  dword     timeOut;        // Wartezeit beim Polling in Mikrosekunden
+  dword     cntTo;          // Zähler für ausbleibende Antworten
+  dword     cntErrCrc;      // Zähler für CRC-Fehler bei der Übertragung
+  dword     cntNakEP;       // Zähler für NAK-Antworten beim Empty-Polling
+  dword     cntAckDP;       // Zähler für ACK-Antworten beim Data-Polling
+  dword     cntLostPdu;     // Zähler für verlorene Telegramme
+  dword     cntLostMeas;    // Zähler für verlorene Messwerte
+  dword     delayCnt;       // Verzögerung (Polldurchläufe) vor Fehlerzählung
+  byte      adr;            // Adresse (Nummer) des Slave (1-255)
+  byte      area;           // Einsatzgebiet des Slave (Adresserweiterung)
+  byte      chn;            // Aktueller Übertragungskanal (0-39)
+  byte      pIdx;           // Index in der aktuellen Pollingliste
+  word      prioSet;        // Anfangspriorität (rel. Häufigkeit) beim Polling
+  word      minPrio;        // Minimale Priorität (maximale Prioritätszahl)
+  PlPduBase result;         // Daten vom Slave
+  PlPduBase control;        // Daten zum Slave
+  bool      newPdu;         // Kennzeichnung für neues Telegramm
+  bool      rspOk;          // Rücksetz-Kennnzeichnung für neues Telegramm
+  bool      newMeas;        // Kennzeichnung für neuen Messwert
+  byte      oldPduCount;    // Merker für die Telegrammüberwachung
+  byte      oldMeasCount;   // Merker für die Messwertüberwachung
+  byte      rspCtrlCount;   // Merker für Steuerungsüberwachung
+} Slave, *SlavePtr;
+
+
+typedef struct _PollInfo
+{
+  dword   aliens;     // Anzahl der netzfremden Aktivitäten
+  dword   wrongs;     // Anzahl ungewünschter Netzaktivitäten
+} PollInfo, *PollInfoPtr;
+
+
+typedef struct _PollState
+{
+  byte    slIdx;      // Index in der Slave-Liste
+  byte    status;     // Zustand
+  word    prioCnt;    // Prioritätszähler
+} PollState, *PollStatePtr;
+
+typedef bool (*cbDataPtr)(PlpType dataType, byte *dest);
+typedef bool (*cbCtrlPtr)(PlpType plpType, byte *dest, byte *src, int sSize);
+
+#define psSlaveWasPresent 0x01
+#define psSlaveIsPresent  0x02
+
+// ----------------------------------------------------------------------------
+//                            B l e P o l l
+// ----------------------------------------------------------------------------
+class BlePoll
+{
+public:
+  // -------------------------------------------------------------------------
+  // Öffentliche Datentypen
+  // -------------------------------------------------------------------------
+  //
+  typedef enum _ComType
+  {
+    ctMASTER,
+    ctSLAVE,
+    ctHybrid
+  } ComType;
+
+  // Identifikator für die Anwendung
+  //
+  typedef enum _AppType
+  {
+    atDefault,        // Standard-Default-Anwendung
+    atTestSend,       // einfacher Sendetest (Soaap)
+    atSOAAP,          // Steuerung optischer und akustischer Ausgaben für Performance-Künstler
+    atDevSOAAP,       // Entwicklerbetrieb für SOAAP
+    atDHA             // Dezentrale Hausautomatisierung
+  } AppType;
+
+  // --------------------------------------------------------------------------
+  // Öffentliche Daten
+  // --------------------------------------------------------------------------
+  //
+  bool    DataExchange;
+
+private:
+  // -------------------------------------------------------------------------
+  // Private Datentypen
+  // -------------------------------------------------------------------------
+  //
+
+  typedef void (BlePoll::*cbVector)(void);
+  typedef dword (*MicsecFuPtr)(void);
+
+  // --------------------------------------------------------------------------
+  // Lokale Daten
+  // --------------------------------------------------------------------------
+  //
+  IntrfRadio    *radio;
+  bcPdu         pduOut;
+  bcPdu         pduIn;
+  cbVector      nextState;
+  MicsecFuPtr   micSec;
+  cbDataPtr     cbData;
+  cbCtrlPtr     cbCtrl;
+  dword         toSet;
+  dword         toValue;
+  dword         cycleMics;
+  dword         cycleCnt;
+  dword         wdTimeOut;
+
+  int           chn;
+  int           adr;
+  int           area;
+  bool          master;
+  bool          eadr;
+  bool          nak;
+  bool          crcError;
+
+  PlMode        plMode;
+  PlMode        oldPlMode;
+  PlpType       plpType;
+
+  Slave         slaveList[MAXSLAVE+1];
+  SlavePtr      curSlave;
+  int           slaveIdx; // ist z.Zt. identisch mit Slaveadresse adr
+  PollState     pollList[MAXSLAVE+1];
+  PollStatePtr  curPoll;
+  int           pollIdx;
+  int           pollNr;
+  int           pollMaxNr;
+
+  int           maxAdr;
+  dword         cntPolling;
+  dword         cntAllRecs;
+  dword         cntAllTo;
+  bool          pollStop;
+  bool          pollStopped;
+
+  dword         cntAllNaks;
+  dword         cntAlien;
+  dword         cntWrong;
+  dword         cntWaitDisabled;
+
+  dword         bleState;
+  dword         runCounter;
+  bool          recStop;
+  bool          recStopped;
+
+  TxStatistics  statistic;
+  PlPduMeas     valuePdu;
+  PlpCtrl27     ctrlPdu;
+  bool          newValue;
+  bool          newCtrl;
+
+  // Einstellungen für den Anwendungsbetrieb
+  //
+  bool          fullCycle;      // Vollständige Anwendung (EP & Data)
+  int           epCycleTotal;   // Anzahl der leeren Pollings gesamt
+  int           epCycleRun;     // Anzahl der leeren Pollings nach Kontakt
+  dword         epTimeOut;      // Time-Out in Mikrosekunden
+
+
+  // --------------------------------------------------------------------------
+  // Lokale Funktionen
+  // --------------------------------------------------------------------------
+  //
+  void setPduAddress();
+  void setPduAddress(bcPduPtr pduPtr);
+  void setTimeOut(dword value);
+  bool timeOut();
+  bool getValues(bcPduPtr pduPtr, PlpType appId);
+  bool getCtrls(bcPduPtr pduPtr, PlpType appId);
+
+
+  // Zustandsmaschine
+  // -----------------------------
+  void smInit();
+  void smIdle();
+
+  // Leeres Polling Master
+  //
+  void smStartEP();
+  void smReqEadr();
+  void smWaitNak();
+  void smEndEP();
+
+  // Leeres Polling Slave
+  //
+  void smWaitEadr();
+  void smEvalPoll();
+
+  // Datenübertragung
+  //
+  void smStartCom();
+
+  // Master: Master -> Slave
+  //
+  void smReqComE();
+  void smWaitAckComE();
+  void smEndComE();
+
+  // Master: Slave -> Master
+  //
+  void smReqComS();
+  void smWaitAckComS();
+  void smEndComS();
+
+  // Slave: Master <-> Slave
+  //
+  void smStartComES();
+  void smWaitComES();
+  void smEvalComES();
+
+  // Test
+  //
+  void smStartTest();
+  void smWaitTest();
+  void smLoopTest();
+
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen
+  // --------------------------------------------------------------------------
+  BlePoll(IntrfRadio *refRadio, dword inCycleMics);
+  BlePoll(IntrfRadio *refRadio, MicsecFuPtr inMicroFu);
+  void init(IntrfRadio *refRadio, dword inCycleMics, MicsecFuPtr inMicroFu);
+
+  void begin(ComType typeIn, int adrIn, AppType appType, dword watchDog);
+  void setCbDataPtr(cbDataPtr cbPtr);
+  void setCbCtrlPtr(cbCtrlPtr cbPtr);
+
+  // --------------------------------------------------------------------------
+  // Konfiguration
+  // --------------------------------------------------------------------------
+  //
+  void setPollAddress(int chnIn, int adrIn, int areaIn, bool masterIn, bool eadrIn, bool nakIn);
+  void setEmptyPollParams(int cycleTotal, int cycleRun, dword timeOut);
+  void setDataPollParams(int slAdr, int prio, int minPrio, dword timeOut);
+
+  // --------------------------------------------------------------------------
+  // Steuerung des Telegrammaustausches (Polling)
+  // --------------------------------------------------------------------------
+  //
+  void run();       // Ablaufsteuerung (CPU-Übergabe) dieses Moduls
+/**
+ * @brief Sendet neue Steuerungsdaten an einen Slave
+ * 
+ * @param adr Addresse d. zu Steuernden Slaves
+ * @param ctrlList Liste mit Bytes zum Austausch der Steuerbytes
+ * @param nr Anzahl d. Steuerbytes
+ */
+  void updControl(int adr, byte *ctrlList, int nr);   // neue Steuerungsdaten
+  //
+/**
+ * @brief Prüft ob Steuerungsdaten übertragen wurden
+ * 
+ * @param adr Adresse d. Slaves
+ * @return true Erfolgreiche übertragung
+ * @return false Fehler in Übertragung / Falsche Adresse
+ */
+  bool ackTrans(int adr);         // Bestätigung Steuerungsdaten übertragen
+  /**
+ * @brief Überprüft ob Steuerungsdaten korrekt übertragen wurden
+ * 
+ * @param adr Adresse d. Slavrs
+ * @return true Daten erfolgreich verarbeitet
+ * @return false Fehler in Übertragung
+ */
+  bool ackControl(int adr);       // Bestätigung Steuerung ausgeführt
+
+
+  // Test
+  //
+
+  // Leeres Polling
+  //
+  void stopEP();
+  void resumeEP();
+  bool stoppedEP();
+
+  // Empfangsbetrieb beim Slave
+  //
+  void stopSR();
+  void resumeSR();
+  bool stoppedSR();
+
+  // Laufender Betrieb
+  //
+  void start(PlMode inPlMode);
+
+
+
+  // --------------------------------------------------------------------------
+  // Zugriff auf Polling-Informationen
+  // --------------------------------------------------------------------------
+  //
+  int   getSlaveList(byte *dest, int maxByte);
+  void  resetPollCounters();
+
+  // --------------------------------------------------------------------------
+  // Zugriff auf Slavedaten
+  // --------------------------------------------------------------------------
+  // Der Index wird von 0 an ausgewertet. Allerdings ist [0] in der Slave-Liste
+  // auf den Index [1] abzubilden, weil Slave[0] für besondere Aufgaben
+  // reserviert und für den Anwender nicht zugänglich ist.
+  //
+  bool      measAvail(int slIdx);   // Feststellen, ob neue Messwerte da sind
+  int       getArea(int slIdx);     // Wert der Area auslesen
+  PlpType   getAppId(int slIdx);    // Wert der AppId (BlePoll) auslesen
+  int       getMeas(int slIdx, byte *dest);    // Messwerte übergeben
+  int       getCtrlM(int slIdx, byte *dest);
+
+
+  // --------------------------------------------------------------------------
+  // Debugging
+  // --------------------------------------------------------------------------
+  //
+  dword debGetDword(int idx);
+  dword getStatistics(TxStatisticsPtr dest);
+
+  SlavePtr      getSlavePtr(int idx);
+  PollStatePtr  getPollPtr(int idx);
+};
+
+
+// ----------------------------------------------------------------------------
+#endif // BlePoll_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/BlePoll/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/BlePoll/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..477225b9ff50f19fdf4a1ffb19f791f80efa4870
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/BlePoll/library.json
@@ -0,0 +1,4 @@
+{
+    "name": "BlePoll",
+    "version": "0.0.0+20220804174235"
+  }
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/ComRingBuf/ComRingBuf.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/ComRingBuf/ComRingBuf.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..66a9784b5507d680cc700b0f7fe5e68379da7e3e
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/ComRingBuf/ComRingBuf.cpp
@@ -0,0 +1,782 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   ComRingBuf.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   21. November 2021
+//
+// Der Inhalt dieser Datei sind Festlegungen zur Gestaltung eines Ringpuffers.
+//
+
+#include "ComRingBuf.h"
+
+  // --------------------------------------------------------------------------
+  // Initialisierungen
+  // --------------------------------------------------------------------------
+
+  ComRingBuf::ComRingBuf()
+  {
+    rbReadIdx = 0;
+    rbWriteIdx = 0;
+    sbReadIdx = 0;
+    sbWriteIdx = 0;
+    newLineMode = NewLineModeNL;
+  }
+
+  void ComRingBuf::begin(IntrfSerial *ser)
+  {
+    serIf = ser;
+  }
+
+  // --------------------------------------------------------------------------
+  // Konfiguration
+  // --------------------------------------------------------------------------
+  //
+  void ComRingBuf::setNewLineMode(byte nlMode)
+  {
+    newLineMode = nlMode;
+  }
+
+
+  // --------------------------------------------------------------------------
+  // Schnittstellen
+  // --------------------------------------------------------------------------
+  //
+  // Byte aus dem Sendepuffer lesen
+  //
+  bool  ComRingBuf::getByteSnd(byte *dest)
+  {
+    if(sbReadIdx == sbWriteIdx) return(false);
+
+    *dest = sndBuffer[sbReadIdx];
+    sbReadIdx++;
+    if(sbReadIdx >= sbSize)
+      sbReadIdx = 0;
+    return(true);
+  }
+
+  // Byte in den Empfangspuffer schreiben
+  //
+  void  ComRingBuf::putByteRec(byte b)
+  {
+    int space = rbReadIdx - rbWriteIdx - 1;
+    if(space == 0) return;
+  }
+
+
+// ----------------------------------------------------------------------------
+// Writing and reading data via circular buffer (default usage)
+// ----------------------------------------------------------------------------
+//
+
+// ----------------------------------------------------------------------------
+// Lesen (Empfangsvorgänge)
+// ----------------------------------------------------------------------------
+
+void  ComRingBuf::setReadBuffer(int size, byte *bufPtr)
+{
+  recBuffer = bufPtr;
+  rbSize = size;
+  rbReadIdx = 0;
+  rbWriteIdx = 0;
+}
+
+int   ComRingBuf::getChr()
+{
+  int retv;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(EOF);
+
+  retv = recBuffer[rbReadIdx];
+  rbReadIdx++;
+  if(rbReadIdx >= rbSize)
+    rbReadIdx = 0;
+  return(retv);
+}
+
+void  ComRingBuf::clrRecBuf()
+{
+  rbReadIdx  = 0;
+  rbWriteIdx = 0;
+}
+
+int   ComRingBuf::getAll(byte *buffer)
+{
+  int   count, i;
+  int   tmpInt;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(EOF);
+
+  tmpInt = rbWriteIdx - rbReadIdx;
+  if(tmpInt < 0)
+    count = tmpInt + rbSize;
+  else
+    count = tmpInt;
+
+  for(i = 0; i < count; i++)
+  {
+    buffer[i] = recBuffer[rbReadIdx];
+    rbReadIdx++;
+    if(rbReadIdx >= rbSize)
+      rbReadIdx = 0;
+  }
+
+  return(count);
+}
+
+int   ComRingBuf::getCount(int len, byte *buffer)
+{
+  int  count, i;
+  int  tmpInt;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(0);
+
+  tmpInt = rbWriteIdx - rbReadIdx;
+  if(tmpInt < 0)
+    count = tmpInt + rbSize;
+  else
+    count = tmpInt;
+
+  if(len > count)
+    len = count;
+
+  for(i = 0; i < len; i++)
+  {
+    buffer[i] = recBuffer[rbReadIdx];
+    rbReadIdx++;
+    if(rbReadIdx >= rbSize)
+      rbReadIdx = 0;
+  }
+
+  return(len);
+}
+
+int   ComRingBuf::getCountStr(int len, char *buffer)
+{
+  int nrChar;
+
+  nrChar = getCount(len, (uint8_t *) buffer);
+  if(nrChar == EOF) return(EOF);
+
+  buffer[nrChar] = 0;
+  return(nrChar);
+}
+
+int   ComRingBuf::getLine(char *buffer)
+{
+  bool      eol;
+  int       count, i;
+  int       tmpInt;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(0);
+
+  tmpInt = rbWriteIdx - rbReadIdx;
+  if(tmpInt < 0)
+    count = tmpInt + rbSize;
+  else
+    count = tmpInt;
+
+  eol = false;
+
+  for(i = 0; i < count; i++)
+  {
+    buffer[i] = recBuffer[rbReadIdx];
+    if(!eol)
+    {
+    if(buffer[i] == '\r' || buffer[i] == '\n')
+      eol = true;
+    }
+    else
+    {
+      if(buffer[i] != '\r' && buffer[i] != '\n')
+        break;
+    }
+    rbReadIdx++;
+    if(rbReadIdx >= rbSize)
+      rbReadIdx = 0;
+  }
+
+  if(!eol) return(0);
+
+  buffer[i] = 0;
+  return(i);
+}
+
+int   ComRingBuf::getLineDec(int *intValue)
+{
+  bool      eol, inVal;
+  int       count, i, j;
+  int       tmpInt;
+  char      c;
+  char      buffer[32];
+
+  if(rbReadIdx == rbWriteIdx)
+    return(0);
+
+  tmpInt = rbWriteIdx - rbReadIdx;
+  if(tmpInt < 0)
+    count = tmpInt + rbSize;
+  else
+    count = tmpInt;
+
+  if(count > 30)
+    count = 30;
+
+  eol = false;
+
+  j = 0;
+  inVal = false;
+
+  for(i = 0; i < count; i++)
+  {
+    c = recBuffer[rbReadIdx];
+    if(!inVal)
+    {
+      if(c > '9')
+      {
+        rbReadIdx++;
+        if(rbReadIdx >= rbSize)
+          rbReadIdx = 0;
+        continue;
+      }
+      inVal = true;
+    }
+
+    if(!eol)
+    {
+    if(c == '\r' || c == '\n')
+      eol = true;
+    }
+    else
+    {
+      if(c != '\r' && c != '\n')
+        break;
+    }
+    rbReadIdx++;
+    if(rbReadIdx >= rbSize)
+      rbReadIdx = 0;
+    buffer[j++] = c;
+  }
+
+  if(!eol) return(0);
+
+  buffer[j] = 0;
+  *intValue = atoi(buffer);
+  return(i);
+}
+
+char ComRingBuf::getC()
+{
+  char retC;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(0);
+
+  retC = recBuffer[rbReadIdx];
+  rbReadIdx++;
+  if(rbReadIdx >= rbSize)
+    rbReadIdx = 0;
+
+  return(retC);
+}
+
+
+int   ComRingBuf::waitLine(int waitLoop, char *buffer)
+{
+  char inChar;
+  bool eol;
+
+  if(loopCount == 0)
+  {
+    tmpIdx = 0;
+  }
+
+  tmpVal = inCount();
+  if(tmpVal < 1)
+  {
+    loopCount++;
+    if(loopCount < waitLoop)
+      return(0);
+    else
+    {
+      loopCount = 0;
+      return(EOF);
+    }
+  }
+
+  eol = false;
+
+  for(int i = 0; i < tmpVal; i++)
+  {
+    inChar = getC();
+    buffer[tmpIdx++] = inChar;
+    if(inChar == '\r' || inChar == '\n')
+    {
+      eol = true;
+      break;
+    }
+  }
+
+  if(eol)
+  {
+    buffer[tmpIdx] = 0;
+    loopCount = 0;
+    return(tmpIdx);
+  }
+
+  return(0);
+}
+
+//int   ComRingBuf::waitLineDec(int waitLoop, int *intValue)
+//{
+//};
+
+int   ComRingBuf::chkLine(char *rsp)
+{
+  int     i,chkVal;
+  char    chkChar;
+
+  chkVal = inCount();
+  if(chkVal <= strlen(rsp))
+    return(0);
+
+  chkVal = 0;
+
+  for(i = 0; i < strlen(rsp); i++)
+  {
+    chkChar = getC();
+    if(rsp[i] != chkChar)
+    {
+      chkVal = -100000;
+      break;
+    }
+    else
+      chkVal++;
+  }
+
+  if(chkVal < 0) chkVal = EOF;
+
+  while( (recBuffer[rbReadIdx] == '\r' || recBuffer[rbReadIdx] == '\n' )
+         && (rbReadIdx != rbWriteIdx))
+  {
+    rbReadIdx++;
+    if(rbReadIdx >= rbSize)
+      rbReadIdx = 0;
+  }
+
+  return(chkVal);
+}
+
+int   ComRingBuf::chkBuf(char *rsp)
+{
+  int     i,chkVal;
+  char    chkChar;
+
+  chkVal = inCount();
+  if(chkVal < strlen(rsp))
+    return(0);
+
+  chkVal = 0;
+
+  for(i = 0; i < strlen(rsp); i++)
+  {
+    chkChar = getC();
+    if(rsp[i] != chkChar)
+      chkVal = -100;
+    else
+      chkVal++;
+  }
+
+  return(chkVal);
+}
+
+int   ComRingBuf::waitAll(int waitLoop, byte *buffer)
+{
+}
+
+int   ComRingBuf::waitChkBuf(int waitLoop, char *rsp)
+{
+  int   i;
+  int   chkVal;
+  char  chkChar;
+
+  if(loopCount == 0)
+  {
+    tmpVal = strlen(rsp);
+  }
+
+  chkVal = inCount();
+  if(chkVal < tmpVal)
+  {
+    loopCount++;
+    if(loopCount < waitLoop)
+      return(0);
+    else
+    {
+      loopCount = 0;
+      return(-10);
+    }
+  }
+
+  chkChar = getC();
+
+  if(rsp[0] != chkChar)
+    return(0);
+
+  if(tmpVal == 1)
+  {
+     loopCount = 0;
+     return(1);
+  }
+
+  chkVal = 1;
+
+  for(i = 1; i < tmpVal; i++)
+  {
+    chkChar = getC();
+    if(rsp[i] != chkChar)
+      return(0);
+    else
+      chkVal++;
+  }
+
+  loopCount = 0;
+  return(chkVal);
+}
+
+int   ComRingBuf::inCount(void)
+{
+  int count = rbWriteIdx - rbReadIdx;
+  if(count < 0)
+    count += rbSize;
+  return(count);
+}
+
+int   ComRingBuf::getRestChar(byte tagChr, int len, byte *buffer)
+{
+  int       count, i, j;
+  byte      inChr;
+  int       tmpInt;
+  bool      tagged;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(0);
+
+  tmpInt = rbWriteIdx - rbReadIdx;
+  if(tmpInt < 0)
+    count = tmpInt + rbSize;
+  else
+    count = tmpInt;
+
+  if(len > count)
+    len = count;
+
+  tagged = false;
+  j = 0;
+
+  for(i = 0; i < len; i++)
+  {
+    inChr = recBuffer[rbReadIdx];
+    rbReadIdx++;
+    if(rbReadIdx >= rbSize)
+      rbReadIdx = 0;
+
+    if(!tagged)
+    {
+      if(inChr != tagChr)
+        continue;
+
+      tagged = true;
+      continue;
+    }
+
+    buffer[j++] = inChr;
+  }
+
+  if(!tagged) j = -1;
+
+  return(j);
+}
+
+int   ComRingBuf::getRestStr(char *tagStr, int len, byte *buffer)
+{
+  int       count, i, j, tmpIdx;
+  byte      inChr;
+  int       tmpInt;
+  bool      tagged;
+  int       tagLen;
+  int       tagIdx;
+
+  if(rbReadIdx == rbWriteIdx)
+    return(0);
+
+  tmpIdx = rbReadIdx;
+  tmpInt = rbWriteIdx - tmpIdx;
+
+  if(tmpInt < 0)
+    count = tmpInt + rbSize;
+  else
+    count = tmpInt;
+
+  if(len > count)
+    len = count;
+
+  tagged = false;
+  j = 0;
+
+  tagLen = (int) strlen(tagStr);
+  tagIdx = 0;
+
+  if(len < tagLen)
+    return(0);
+
+  for(i = 0; i < len; i++)
+  {
+    inChr = recBuffer[tmpIdx];
+    tmpIdx++;
+    if(tmpIdx >= rbSize)
+      tmpIdx = 0;
+
+    if(!tagged)
+    {
+      if(inChr != tagStr[tagIdx])
+      {
+        tagIdx = 0;
+        continue;
+      }
+
+      tagIdx++;
+
+      if(tagIdx == tagLen)
+        tagged = true;
+      continue;
+    }
+
+    buffer[j++] = inChr;
+  }
+
+  if(!tagged) return(EOF);
+  else
+  {
+    rbReadIdx = tmpIdx;
+    return(j);
+  }
+}
+
+int   ComRingBuf::reqChkLine(char *req, char *rsp)
+{
+  int   i;
+  int   chkVal;
+  char  chkChar;
+
+  switch(reqChkState)
+  {
+    case 0:
+      rbReadIdx  = 0;
+      rbWriteIdx = 0;
+      chkVal = putLine(req);
+      if(chkVal <= 0)
+        return(EOF);
+      tmpVal = strlen(rsp);
+      reqChkState = 1;
+      return(0);
+
+    case 1:
+      chkVal = inCount();
+      if(chkVal <= tmpVal)
+        return(0);
+      chkVal = 0;
+
+      for(i = 0; i < tmpVal; i++)
+      {
+        chkChar = getC();
+        if(rsp[i] != chkChar)
+          chkVal = -100;
+        else
+          chkVal++;
+      }
+
+      while(     (recBuffer[rbReadIdx] == '\r' || recBuffer[rbReadIdx] == '\n')
+          && (rbReadIdx != rbWriteIdx) )
+      {
+        rbReadIdx++;
+        if(rbReadIdx >= rbSize)
+          rbReadIdx = 0;
+      }
+
+      reqChkState = 0;
+      return(chkVal);
+  }
+
+  return(-1000);    // internal error with <reqChkState>
+}
+
+// ----------------------------------------------------------------------------
+// Schreiben (Sendevorgänge)
+// ----------------------------------------------------------------------------
+
+void  ComRingBuf::setWriteBuffer(int size, byte *bufPtr)
+{
+  sndBuffer = bufPtr;
+  sbSize = size;
+  sbReadIdx = 0;
+  sbWriteIdx = 0;
+}
+
+int   ComRingBuf::putChr(int chr)
+{
+  int       space;
+  bool      txDone;
+
+  if(sndBuffer == NULL) return(EOF);
+  if(serIf == NULL) return(EOF);
+
+  space = getSpace();
+
+  if(space == 0)
+  {
+    // Wenn der Sendepuffer voll ist, dann kann der Entlader feststecken
+    serIf->resuSend();
+    return(EOF);
+  }
+
+  if(sbReadIdx == sbWriteIdx)
+  {
+    // Wenn der Sendepuffer leer ist, dann kann das Zeichen evt.
+    // direkt gesendet werden.
+    txDone = serIf->condSend(chr);
+    if(txDone) return(chr);
+  }
+
+  putBufB(chr);
+  return(chr);
+}
+
+int   ComRingBuf::putStr(char *msg)
+{
+  int sIdx = 0;
+
+  if(sndBuffer == NULL) return(EOF);
+  if(serIf == NULL) return(EOF);
+
+  int space = getSpace();
+  int len = strlen(msg);
+  if(space < len)
+  {
+    serIf->resuSend();
+    return(EOF);
+  }
+
+  if(sbReadIdx == sbWriteIdx)
+  {
+    // Wenn der Sendepuffer leer ist, dann kann das erste Zeichen evt.
+    // direkt gesendet werden.
+    if(serIf->condSend(msg[0]))
+      sIdx = 1;
+  }
+
+  for (int i = sIdx; i < len; i++)
+   {
+     sndBuffer[sbWriteIdx] = msg[i];
+     sbWriteIdx++;
+     if(sbWriteIdx >= sbSize)
+       sbWriteIdx = 0;
+   }
+
+  return(len);
+}
+
+int   ComRingBuf::putSeq(byte *msg, int n)
+{
+  int sIdx = 0;
+
+  if(sndBuffer == NULL) return(EOF);
+  if(serIf == NULL) return(EOF);
+
+  int space = getSpace();
+
+  if(space < n)
+  {
+    serIf->resuSend();
+    return(EOF);
+  }
+
+  if(sbReadIdx == sbWriteIdx)
+  {
+    // Wenn der Sendepuffer leer ist, dann kann das erste Zeichen evt.
+    // direkt gesendet werden.
+    if(serIf->condSend(msg[0]))
+      sIdx = 1;
+  }
+
+  for (int i = sIdx; i < n; i++)
+   {
+     sndBuffer[sbWriteIdx] = msg[i];
+     sbWriteIdx++;
+     if(sbWriteIdx >= sbSize)
+       sbWriteIdx = 0;
+   }
+
+  return(n);
+
+}
+
+int   ComRingBuf::putNL()
+{
+  int retv = 0;
+
+  if(newLineMode & NewLineModeCR)
+  {
+    putBufB('\r');
+    retv++;
+  }
+
+  if(newLineMode & NewLineModeNL)
+  {
+    putBufB('\n');
+    retv++;
+  }
+
+  return(retv);
+}
+
+int   ComRingBuf::putLine(char *msg)
+{
+  int retv, nl;
+
+  retv = putStr(msg);
+  if(retv < 0)
+    return(retv);
+
+  nl = putNL();
+  return(retv);
+}
+
+//int   ComRingBuf::putLine(char *msg, char c)
+//{
+//}
+
+//int   ComRingBuf::putLine(char *msg, int n)
+//{
+//}
+
+
+  // --------------------------------------------------------------------------
+  // Debugging
+  // --------------------------------------------------------------------------
+  //
+
+
+
+
+
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/ComRingBuf/ComRingBuf.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/ComRingBuf/ComRingBuf.h
new file mode 100644
index 0000000000000000000000000000000000000000..bd8ee0c8e604ac8ca5a6c344ca9966bb856f365b
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/ComRingBuf/ComRingBuf.h
@@ -0,0 +1,227 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   ComRingBuf.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   21. November 2021
+//
+// Der Inhalt dieser Datei sind Festlegungen zur Gestaltung eines Ringpuffers.
+//
+
+#ifndef ComRingBuf_h
+#define ComRingBuf_h
+// ----------------------------------------------------------------------------
+
+#include "stddef.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+#include "arduinoDefs.h"
+#include "IntrfBuf.h"
+#include "IntrfSerial.h"
+
+#define NewLineModeCR     0x01
+#define NewLineModeNL     0x02
+
+// ----------------------------------------------------------------------------
+//                            C o m R i n g B u f
+// ----------------------------------------------------------------------------
+class ComRingBuf : IntrfBuf
+{
+private:
+  // --------------------------------------------------------------------------
+  // Lokale Daten
+  // --------------------------------------------------------------------------
+  //
+  // Zugang zur Peripherie
+  //
+  IntrfSerial *serIf;
+
+  // Lesen und Schreiben von Zeichen (Bytes)
+  //
+  byte      *ptrSend;         // Der (veraenderliche) Sendezeiger
+  byte      *ptrRec;          // Der (veraenderliche) Empfangszeiger
+  int       maxRec;           // Maximale Anzahl zu empfangender Bytes
+  byte      endChrRec;        // Abschlusszeichen beim Empfang
+  byte      condMaskCom;      // Bedingungen fuer den Datenaustausch
+  byte      newLineMode;      // Art für eine neue Zeile (CR/LF)
+
+  byte      *recBuffer;       // Receive ring buffer start address
+  word      rbReadIdx;        // Read index
+  word      rbWriteIdx;       // Write index
+  word      rbSize;           // Buffer size
+
+  byte      *sndBuffer;       // Transmit ring buffer start address
+  word      sbReadIdx;        // Read index
+  word      sbWriteIdx;       // Write index
+  word      sbSize;           // Buffer size
+
+  int       loopCount;        // For internal time out checking
+  int       reqChkState;      // State of request/check procedure
+  int       tmpVal;           // Variable for temporary data storage
+  int       tmpIdx;           // Variable for temporary array index
+
+
+  // --------------------------------------------------------------------------
+  // Lokale Funktionen
+  // --------------------------------------------------------------------------
+  //
+  char  getC();
+  int   putNL();
+
+  // --------------------------------------------------------------------------
+  // Inline-Funktionen
+  // --------------------------------------------------------------------------
+  //
+  void putBufB(byte b)
+  {
+    sndBuffer[sbWriteIdx] = b;
+    sbWriteIdx++;
+    if(sbWriteIdx >= sbSize)
+      sbWriteIdx = 0;
+  }
+
+  int getSpace()
+  {
+    int space = sbReadIdx - sbWriteIdx - 1;
+    if(space < 0) space += sbSize;
+    return(space);
+  }
+
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen
+  // --------------------------------------------------------------------------
+  ComRingBuf();
+
+  void begin(IntrfSerial *ser);
+
+  // --------------------------------------------------------------------------
+  // Konfiguration
+  // --------------------------------------------------------------------------
+  //
+  void setNewLineMode(byte nlMode);
+
+  // --------------------------------------------------------------------------
+  // Schnittstellen
+  // --------------------------------------------------------------------------
+  //
+  bool  getByteSnd(byte *dest);
+  void  putByteRec(byte b);       // Byte vom Empfang an Puffer geben
+
+
+  // Zuweisen eines Speichers (*bufPtr) der Größe size für den Lesepuffer
+  //
+  void  setReadBuffer(int size, byte *bufPtr);
+
+
+  // --------------------------------------------------------------------------
+  // Steuerung
+  // --------------------------------------------------------------------------
+  //
+
+  // ----------------------------------------------
+  // Ein einzelnes Zeichen aus dem Ringpuffer lesen
+  // ----------------------------------------------
+  // Rückgabe EOF (-1), wenn kein Zeichen vorhanden
+  // sonst das älteste Zeichen aus dem Ringpuffer
+  //
+  int   getChr();
+
+  // ----------------------------------------------
+  // Löschen des Emmpfangspuffers
+  // ----------------------------------------------
+  //
+  void  clrRecBuf();
+
+  // --------------------------------------------------
+  // Alle empfangenen Zeichen aus dem Ringpuffer lesen
+  // --------------------------------------------------
+  // Rückgabe EOF (-1), wenn kein Zeichen vorhanden
+  // sonst die Anzahl der empfangenen Zeichen
+  //
+  int   getAll(byte *buffer);
+
+  // ----------------------------------------------------------
+  // Begrenzte Anzahl empfangener Zeichen aus Ringpuffer lesen
+  // ----------------------------------------------------------
+  // Rückgabe EOF (-1), wenn kein Zeichen vorhanden
+  // sonst die Anzahl der ausgelesenen Zeichen
+  //
+  int   getCount(int count, byte *buffer);
+
+  // ------------------------------------------------------------------------
+  // Begrenzte Anzahl Zeichen als 0-terminierten String aus Ringpuffer lesen
+  // ------------------------------------------------------------------------
+  // Rückgabe EOF (-1), wenn kein Zeichen vorhanden
+  // sonst die Anzahl der ausgelesenen Zeichen
+  //
+  int   getCountStr(int count, char *buffer);
+
+  // ---------------------------------------------------------
+  // Die nächste Zeile (Zeichen bis CR und/oder LF) als String
+  // ---------------------------------------------------------
+  // Rückgabe EOF (-1), wenn kein Zeichen vorhanden
+  // sonst die Anzahl der ausgelesenen Zeichen
+  //
+  int   getLine(char *buffer);
+
+  // -----------------------------------------------
+  // Die nächste im Puffer enthaltene Zeile auslesen
+  // und die darin enthaltene Dezimalzahl übergeben
+  // -----------------------------------------------
+  // Rückgabe EOF (-1), wenn kein Zeichen vorhanden
+  // sonst die Anzahl der ausgelesenen Zeichen
+  // oder 0, wenn keine Dezimalzahl enthalten war
+  //
+  int   getLineDec(int *intValue);
+
+  // ---------------------------------------------------------
+  // Warten auf Zeile (Zeichen bis CR und/oder LF) als String
+  // ---------------------------------------------------------
+  // Rückgabe EOF (-1), wenn Wartezyklen verstrichen
+  // Rückgabe 0, solange kein Zeilenende gelesen
+  // sonst die Anzahl der ausgelesenen Zeichen
+  //
+  int   waitLine(int waitLoop, char *buffer);
+
+  //int   waitLineDec(int waitLoop, int *intValue);
+
+  // ---------------------------------------------------------
+  // Testen der Zeile im Puffer
+  // ---------------------------------------------------------
+  // Rückgabe 0, wenn Teststring (noch) nicht enthalten
+  // sonst die Länge des Teststring
+  // EOF, wenn Zeile im Puffer (gelöscht) nicht passte
+  //
+  int   chkLine(char *rsp);
+
+  int   chkBuf(char *rsp);
+  int   waitAll(int waitLoop, byte *buffer);
+  int   waitChkBuf(int waitLoop, char *rsp);
+  int   inCount(void);
+  int   getRestChar(byte tagChr, int len, byte *buffer);
+  int   getRestStr(char *tagStr, int len, byte *buffer);
+  int   reqChkLine(char *req, char *rsp);
+
+  void  setWriteBuffer(int size, byte *bufPtr);
+  int   putChr(int chr);
+  int   putStr(char *msg);
+  int   putSeq(byte *msg, int n);
+  int   putLine(char *msg);
+  //int   putLine(char *msg, char c);
+  //int   putLine(char *msg, int n);
+
+  // --------------------------------------------------------------------------
+  // Debugging
+  // --------------------------------------------------------------------------
+  //
+
+};
+
+
+// ----------------------------------------------------------------------------
+#endif // beacon_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/ComRingBuf/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/ComRingBuf/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..054f2c8c3093791dc7ce4f7de879b26a019e900b
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/ComRingBuf/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "BlePoll",
+  "version": "0.0.0+20220804174235"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/LoopCheck/LoopCheck.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/LoopCheck/LoopCheck.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b01795b4f033ebb0286340840a3b1507c6ef0302
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/LoopCheck/LoopCheck.cpp
@@ -0,0 +1,838 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Software Loop Checking and Timing
+// Datei:   LoopCheck.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (siehe Wikipedia: Creative Commons)
+//
+
+#include "LoopCheck.h"
+
+
+  // -------------------------------------------------------------------------
+  // Konstruktoren und Initialisierungen
+  // -------------------------------------------------------------------------
+  //
+  LoopCheck::LoopCheck()
+  {
+    firstLoop       = true;
+    taskHappened    = false;
+    toggleMilli     = true;
+
+    initStatistics();
+    initTasks();
+    initClock();
+  }
+
+  void LoopCheck::initStatistics()
+  {
+    backgroundMicros        = 0;
+    loopMicros              = 0;
+    loopStartMicros         = 0;
+    loopEndMicros           = 0;
+    backgroundMaxMicros     = 0;
+    backgroundMinMicros     = (unsigned long) -1;
+    backgroundAvgMicros     = 0;
+    loopMaxMicros           = 0;
+    loopMinMicros           = (unsigned long) -1;
+    loopAvgMicros           = 0;
+    loopCounter             = 0;
+    periodFailCount         = 0;
+    periodMaxMicros         = 0;
+    periodMinMicros         = (unsigned int) -1;
+    periodMicros            = 0;
+    periodFailAlarm         = false;
+
+    year    = 0;
+    day     = 0;
+    hour    = 0;
+    min     = 0;
+    sec     = 0;
+    msec    = 0;
+
+    measureRuntime = 0;
+
+    calcAvgCounter = 0;
+  }
+
+  void LoopCheck::initTasks()
+  {
+    for (int i = 0; i < NrOfTimerTasks; i++)
+    {
+      timerTaskList[i].counterStarted    = false;
+      timerTaskList[i].finished          = false;
+      timerTaskList[i].firstRun          = true;
+      timerTaskList[i].runCounter        = 0;
+    }
+
+    for( int i = 0; i < NrOfOnceTasks; i++)
+    {
+      onceTaskList[i].finished      = false;
+      onceTaskList[i].firstRun      = true;
+      onceTaskList[i].waitCounter   = 0;
+    }
+  }
+
+  void LoopCheck::initClock()
+  {
+    strcpy(dateTimeStr,"2017-12-07T17:11:35.456+00:00");
+    dtYear      = 2017;
+    dtMonth     = 12;
+    dtDay       = 7;
+    dtHour      = 17;
+    dtMin       = 11;
+    dtSec       = 35;
+    dtmSec      = 456;
+  }
+
+  // -------------------------------------------------------------------------
+  // Anwenderfunktionen
+  // -------------------------------------------------------------------------
+  //
+
+  void LoopCheck::begin()
+  {
+    unsigned int    cycleMillis;
+    unsigned int    restMicros;
+    unsigned int    tmpInt;
+    unsigned int    tmpInt100, tmpInt10, tmpInt1;
+    div_t           divResult;
+
+    loopStartMicros = SYSMICSEC;
+    clockCycleMicros = loopStartMicros - lastClockMicros + lastRestMicros;
+    //
+    // Zeit seit dem letzten Aufruf von begin()
+
+  
+    //
+    if(firstLoop == true)
+    {
+      clockCycleMicros = 0;
+      lastClockMicros = loopStartMicros;
+    }
+
+    // Aufteilen in Millisekunden und Mikrosekunden
+    //
+    divResult = DIV((int) clockCycleMicros, 1000);
+
+    restMicros = divResult.rem;
+    cycleMillis = divResult.quot;
+
+    if(cycleMillis > 0)
+    {
+      lastRestMicros = restMicros;
+      lastClockMicros = loopStartMicros;
+      msec += cycleMillis;
+      dtmSec += cycleMillis;
+
+      // Betriebsstundenzähler
+      //
+      if(msec >= 1000)
+      {
+        msec -= 1000;
+        sec++;
+        measureRuntime++;
+
+        if(sec == 60)
+        {
+          sec = 0;
+          min++;
+          if(min == 60)
+          {
+            min = 0;
+            hour++;
+            if(hour == 24)
+            {
+              hour = 0;
+              day++;
+              if(day == 365)
+              {
+                day = 0;
+                year++;
+              }
+            }
+          }
+        }
+      }
+
+      // Software-Uhr
+      //
+      if(dtmSec >= 1000)
+      {
+        dtmSec -= 1000;
+
+        dtSec++;
+        dateTimeStr[20] = '0';
+        dateTimeStr[21] = '0';
+        dateTimeStr[22] = '0';
+
+        if(dtSec == 60)
+        {
+          dtSec = 0;
+          dtMin++;
+          dateTimeStr[17] = '0';
+          dateTimeStr[18] = '0';
+
+          if(dtMin == 60)
+          {
+            dtMin = 0;
+            dtHour++;
+            dateTimeStr[14] = '0';
+            dateTimeStr[15] = '0';
+
+            if(dtHour == 24)
+            {
+              dtHour = 0;
+              dtDay++;
+              dateTimeStr[11] = '0';
+              dateTimeStr[12] = '0';
+
+              if  (
+                       (dtDay == (febLen + 1) && dtMonth == 2)
+                    || (dtDay == 31 && (dtMonth == 4 ||
+                                        dtMonth == 6 ||
+                                        dtMonth == 9 ||
+                                        dtMonth == 11))
+                    || dtDay == 32
+                  )
+              {
+                dtDay = 1;
+                dtMonth++;
+                dateTimeStr[8] = '0';
+                dateTimeStr[9] = '1';
+
+                if(dtMonth == 13)
+                {
+                  dtMonth = 1;
+                  dtYear++;
+                  tmpInt = dtYear - 2000;
+                  if((tmpInt % 4) == 0)
+                    febLen = 29;
+                  else
+                    febLen = 28;
+                  dateTimeStr[5] = '0';
+                  dateTimeStr[6] = '1';
+
+                  divResult   = DIV(tmpInt,100);
+                  tmpInt100   = divResult.quot;
+                  tmpInt      = divResult.rem;
+
+                  divResult   = DIV(tmpInt,10);
+                  tmpInt10    = divResult.quot;
+                  tmpInt1     = divResult.rem;
+
+                  dateTimeStr[1] = (char) (tmpInt100 | 0x30);
+                  dateTimeStr[2] = (char) (tmpInt10  | 0x30);
+                  dateTimeStr[3] = (char) (tmpInt1   | 0x30);
+                }
+                else
+                {
+                  divResult      = DIV(dtMonth,10);
+                  dateTimeStr[5] = (char) (divResult.quot | 0x30);
+                  dateTimeStr[6] = (char) (divResult.rem  | 0x30);
+                }
+              }
+              else
+              {
+                divResult      = DIV(dtDay,10);
+                dateTimeStr[8] = (char) (divResult.quot | 0x30);
+                dateTimeStr[9] = (char) (divResult.rem  | 0x30);
+              }
+            }
+            else
+            {
+              divResult       = DIV(dtHour,10);
+              dateTimeStr[11] = (char) (divResult.quot | 0x30);
+              dateTimeStr[12] = (char) (divResult.rem  | 0x30);
+            }
+          }
+          else
+          {
+            divResult       = DIV(dtMin,10);
+            dateTimeStr[14] = (char) (divResult.quot | 0x30);
+            dateTimeStr[15] = (char) (divResult.rem  | 0x30);
+          }
+        }
+        else
+        {
+          divResult     = DIV(dtSec,10);
+          dateTimeStr[17] = (char) (divResult.quot | 0x30);
+          dateTimeStr[18] = (char) (divResult.rem  | 0x30);
+        }
+      }
+      else
+      {
+        divResult   = DIV(dtmSec,100);
+        tmpInt100   = divResult.quot;
+        tmpInt      = divResult.rem;
+
+        divResult   = DIV(tmpInt,10);
+        tmpInt10    = divResult.quot;
+        tmpInt1     = divResult.rem;
+
+        dateTimeStr[20] = (char) (tmpInt100 | 0x30);
+        dateTimeStr[21] = (char) (tmpInt10  | 0x30);
+        dateTimeStr[22] = (char) (tmpInt1   | 0x30);
+      }
+
+    }
+
+    if(firstLoop == false)
+    {
+      backgroundMicros = loopStartMicros - loopEndMicros;
+      if(backgroundMicros > backgroundMaxMicros)
+        backgroundMaxMicros = backgroundMicros;
+      if(backgroundMicros < backgroundMinMicros)
+        backgroundMinMicros = backgroundMicros;
+      periodMicros = loopStartMicros - lastStartMicros;
+      if(periodMicros > periodMaxMicros)
+        periodMaxMicros = periodMicros;
+      periodSumMicros += periodMicros;
+      if(periodMicros < periodMinMicros)
+        periodMinMicros = periodMicros;
+      if(periodMicros > PeriodMinTime)
+      {
+        periodFailAlarm = true;
+        periodFailCount++;
+      }
+
+      divResult = DIV(periodMicros, 1000);
+      if(divResult.quot > 0)
+      {
+        if(divResult.quot >= LoopScreeningGrades)
+          ++loopScreening[LoopScreeningGrades - 1];
+        else
+          ++loopScreening[divResult.quot -1];
+      }
+    } // if()
+
+    lastStartMicros = loopStartMicros;
+
+  }
+
+
+  unsigned int LoopCheck::done()
+  {
+    return(SYSMICSEC - loopStartMicros);
+  }
+
+  void LoopCheck::end()
+  {
+    loopEndMicros = SYSMICSEC;
+    loopMicros = loopEndMicros - loopStartMicros;
+    if(loopMicros > loopMaxMicros)
+      loopMaxMicros = loopMicros;
+    if(loopMicros < loopMinMicros)
+      loopMinMicros = loopMicros;
+
+    if(firstLoop == false)
+    {
+      loopSumMicros += loopMicros;
+      backgroundSumMicros += backgroundMicros;
+      calcAvgCounter++;
+      if(calcAvgCounter == CalcAverageDepth)
+      {
+        loopAvgMicros = loopSumMicros / CalcAverageDepth;
+        backgroundAvgMicros = backgroundSumMicros / CalcAverageDepth;
+        periodAvgMicros = periodSumMicros / CalcAverageDepth;
+        calcAvgCounter = 0;
+        loopSumMicros = 0;
+        backgroundSumMicros = 0;
+        periodSumMicros = 0;
+      }
+    }
+    else
+    {
+      loopAvgMicros = loopMicros;
+      backgroundAvgMicros = backgroundMicros;
+    }
+
+    loopCounter++;
+    firstLoop = false;
+    taskHappened = false;
+  }
+
+  bool LoopCheck::timerMicro
+    (int taskIdx, unsigned long repeatTime, unsigned int repetitions, unsigned long delay)
+  {
+    TimerTask     *ctrlPtr;
+    unsigned long calcMics;
+
+    // Test the limit of enabled timers
+    //
+    if(taskIdx < 0) return(false);
+    if(taskIdx >= NrOfTimerTasks) return(false);
+
+    // Get the reference to timer data for the selected timer
+    //
+    ctrlPtr = &timerTaskList[taskIdx];
+
+    // If the timer task has finished, we are ready here
+    //
+    if(ctrlPtr->finished == true) return(false);
+
+    // If it is the first run (initialisation 1)
+    //
+    if(ctrlPtr->firstRun == true)
+    {
+      ctrlPtr->firstRun     = false;
+      ctrlPtr->repCounter   = repetitions;
+      ctrlPtr->delayCounter = delay;
+    }
+
+    // If counting is not started yet (initialisation 2)
+    //
+    if(ctrlPtr->counterStarted == false)
+    {
+      ctrlPtr->startCount = loopStartMicros;
+      ctrlPtr->counterStarted = true;
+      return(false);
+    }
+
+    // If another count task has happened in this loop, we have to wait
+    //
+    if(taskHappened == true) return(false);
+
+    // Calculate the number of microseconds since the start of the counter
+    //
+    calcMics = loopStartMicros - ctrlPtr->startCount;
+    ctrlPtr->ticks = calcMics;
+
+    // If there is a delay, wait the delay time
+    //
+    if(ctrlPtr->delayCounter > 0)
+    {
+      if(calcMics < ctrlPtr->delayCounter)
+        return(false);
+      else
+      {
+        ctrlPtr->delayCounter = 0;                // delay finished
+        ctrlPtr->startCount = loopStartMicros;    // reset counter
+      }
+      return(false);
+    }
+
+    // There is no delay (or delay is finished)
+    // If repeatTime is not passed, leave with FALSE
+    //
+    if(calcMics < repeatTime)
+    {
+      return(false);
+    }
+
+    // One counter period finished
+    //
+    taskHappened            = true;               // disable other timers in this loop
+    ctrlPtr->counterStarted = false;              // prepare resetting the counter
+    ctrlPtr->runCounter++;                        // count the timer events
+
+    // If the number of periods is limited finish in time
+    //
+    if(ctrlPtr->repCounter > 0)
+    {
+      ctrlPtr->repCounter--;
+      if(ctrlPtr->repCounter == 0)
+        ctrlPtr->finished = true;
+    }
+
+    return(true);
+  }
+
+  bool LoopCheck::timerMicro
+    (int taskIdx, unsigned long repeatTime, unsigned int repetitions)
+  {
+    return(timerMicro(taskIdx, repeatTime, repetitions, 0));
+  }
+
+
+  bool LoopCheck::timerMilli
+    (int taskIdx, unsigned long repeatTime, unsigned int repetitions, unsigned long delay)
+  {
+    return(timerMicro(taskIdx, repeatTime * 1000, repetitions, delay * 1000));
+  }
+
+  bool LoopCheck::timerMilli
+    (int taskIdx, unsigned long repeatTime, unsigned int repetitions)
+  {
+    return(timerMicro(taskIdx,repeatTime * 1000,repetitions,0));
+  }
+
+  bool LoopCheck::once(int taskIdx)
+  {
+    if(taskIdx < 0) return(false);
+    if(taskIdx >= NrOfOnceTasks) return(false);
+
+    if(onceTaskList[taskIdx].finished == true) return(false);
+    onceTaskList[taskIdx].finished = true;
+    return(true);
+  }
+
+  bool LoopCheck::once(int taskIdx, unsigned int nrOfLoops)
+  {
+    if(taskIdx < 0) return(false);
+    if(taskIdx >= NrOfOnceTasks) return(false);
+
+    if(onceTaskList[taskIdx].finished == true) return(false);
+    if(nrOfLoops <= 1)
+    {
+      onceTaskList[taskIdx].finished = true;
+      return(true);
+    }
+
+    if(onceTaskList[taskIdx].firstRun == true)
+    {
+      onceTaskList[taskIdx].firstRun = false;
+      onceTaskList[taskIdx].waitCounter = nrOfLoops;
+    }
+
+    onceTaskList[taskIdx].waitCounter--;
+    if(onceTaskList[taskIdx].waitCounter > 0)
+      return(false);
+
+    onceTaskList[taskIdx].finished = true;
+    return(true);
+  }
+
+  bool LoopCheck::onceDelayed(int taskIdx, unsigned long delay)
+  {
+    if(taskIdx < 0) return(false);
+    if(taskIdx >= NrOfOnceTasks) return(false);
+
+    if(onceTaskList[taskIdx].finished == true) return(false);
+
+    if(onceTaskList[taskIdx].firstRun == true)
+    {
+      onceTaskList[taskIdx].firstRun = false;
+      onceTaskList[taskIdx].startCount = loopStartMicros;
+    }
+
+    if(loopStartMicros - onceTaskList[taskIdx].startCount < delay)
+      return(false);
+
+    onceTaskList[taskIdx].finished = true;
+    return(true);
+  }
+
+  bool LoopCheck::toggle(int taskIdx)
+  {
+    bool    toggleBit;
+
+    if(taskIdx < 0) return(false);
+    if(taskIdx >= NrOfToggleTasks) return(false);
+    toggleBit = toggleTaskList[taskIdx];
+    toggleTaskList[taskIdx] = !toggleBit;
+    return(toggleBit);
+  }
+
+  unsigned long LoopCheck::timerCycle(int taskIdx)
+  {
+    if(taskIdx < 0) return(0);
+    if(taskIdx >= NrOfTimerTasks) return(0);
+    return(timerTaskList[taskIdx].runCounter);
+  }
+
+  bool LoopCheck::timerCycleMod(int taskIdx, int modulo)
+  {
+    div_t divResult;
+
+    if(taskIdx < 0) return(0);
+    if(taskIdx >= NrOfTimerTasks) return(0);
+    divResult   = DIV(timerTaskList[taskIdx].runCounter,modulo);
+    if(divResult.rem == 0)
+      return(true);
+    else
+      return(false);
+  }
+
+  unsigned long LoopCheck::tick(int taskIdx)
+  {
+    if(taskIdx < 0) return(0);
+    if(taskIdx >= NrOfTimerTasks) return(0);
+    return(timerTaskList[taskIdx].ticks);
+  }
+
+  unsigned long LoopCheck::operationTime(OpHourMeter *opHourMeter)
+  {
+    opHourMeter->Milliseconds    = msec;
+    opHourMeter->Seconds         = sec;
+    opHourMeter->Minutes         = min;
+    opHourMeter->Hours           = hour;
+    opHourMeter->Days            = day;
+    opHourMeter->Years           = year;
+    return(loopStartMicros);
+  }
+
+  unsigned long LoopCheck::getStatistics(LoopStatistics *statistics)
+  {
+    statistics->loopTime    =   (unsigned int) loopMicros;
+    statistics->loopMaxTime =   (unsigned int) loopMaxMicros;
+    statistics->loopMinTime =   (unsigned int) loopMinMicros;
+    statistics->loopAvgTime =   (unsigned int) loopAvgMicros;
+
+    statistics->bgTime      =   (unsigned int) backgroundMicros;
+    statistics->bgMaxTime   =   (unsigned int) backgroundMaxMicros;
+    statistics->bgMinTime   =   (unsigned int) backgroundMinMicros;
+    statistics->bgAvgTime   =   (unsigned int) backgroundAvgMicros;
+
+    statistics->alarmCount  =   periodFailCount;
+    statistics->periodAlarm =   periodFailAlarm;
+    periodFailAlarm = false;
+
+    statistics->loopPeriod  =   periodMicros;
+    statistics->maxPeriod   =   periodMaxMicros;
+    statistics->minPeriod   =   periodMinMicros;
+    statistics->avgPeriod   =   periodAvgMicros;
+
+    for (int i = 0; i < LoopScreeningGrades; i++)
+      statistics->rtScreening[i] = loopScreening[i];
+
+    return(loopCounter);
+  }
+
+  void LoopCheck::resetStatistics()
+  {
+    backgroundMicros        = 0;
+    loopMicros              = 0;
+
+    backgroundMaxMicros     = 0;
+    backgroundMinMicros     = (unsigned long) -1;
+    backgroundAvgMicros     = 0;
+
+    loopMaxMicros           = 0;
+    loopMinMicros           = (unsigned long) -1;
+    loopAvgMicros           = 0;
+    loopCounter             = 0;
+
+    periodFailCount         = 0;
+    periodMaxMicros         = 0;
+    periodMinMicros         = (unsigned int) -1;
+    periodAvgMicros         = 0;
+    periodMicros            = 0;
+    periodFailAlarm         = false;
+
+    for (int i = 0; i < LoopScreeningGrades; i++)
+      loopScreening[i] = 0;
+
+    calcAvgCounter          = 0;
+  }
+
+  bool LoopCheck::setDateTime(const char *dtStr)
+  {
+    int tmpInt;
+
+     if(strlen(dtStr) < 23) return(false);
+     strcpy(dateTimeStr,dtStr);
+     dtYear   = (dateTimeStr[0]  & 0x0F) * 1000 +
+                (dateTimeStr[1]  & 0x0F) * 100  +
+                (dateTimeStr[2]  & 0x0F) * 10   +
+                (dateTimeStr[3]  & 0x0F);
+
+     tmpInt = dtYear - 2000;
+     if((tmpInt % 4) == 0)
+       febLen = 29;
+     else
+       febLen = 28;
+
+
+     dtMonth  = (dateTimeStr[5]  & 0x0F) * 10   +
+                (dateTimeStr[6]  & 0x0F);
+
+     dtDay    = (dateTimeStr[8]  & 0x0F) * 10   +
+                (dateTimeStr[9]  & 0x0F);
+
+     dtHour   = (dateTimeStr[11] & 0x0F) * 10   +
+                (dateTimeStr[12] & 0x0F);
+
+     dtMin    = (dateTimeStr[14] & 0x0F) * 10   +
+                (dateTimeStr[15] & 0x0F);
+
+     dtSec    = (dateTimeStr[17] & 0x0F) * 10   +
+                (dateTimeStr[18] & 0x0F);
+
+     dtmSec   = (dateTimeStr[20] & 0x0F) * 10   +
+                (dateTimeStr[21] & 0x0F) * 10   +
+                (dateTimeStr[22] & 0x0F);
+
+     return(true);
+  }
+
+  bool LoopCheck::setDateTime(lcDateTime dt)
+  {
+    div_t   divResult;
+    int     tmpInt;
+
+    dtYear    = dt.Year;
+
+    tmpInt = dtYear - 2000;
+    if((tmpInt % 4) == 0)
+      febLen = 29;
+    else
+      febLen = 28;
+
+
+    divResult = DIV(dtYear,1000);
+    dateTimeStr[0] = (char) (0x30 + divResult.quot);
+
+    divResult = DIV(divResult.rem,100);
+    dateTimeStr[1] = (char) (0x30 + divResult.quot);
+
+    divResult = DIV(divResult.rem,10);
+    dateTimeStr[2] = (char) (0x30 + divResult.quot);
+    dateTimeStr[3] = (char) (0x30 + divResult.rem);
+
+    dtMonth   = dt.Month;
+    divResult = DIV(dtMonth,10);
+    dateTimeStr[5] = (char) (0x30 + divResult.quot);
+    dateTimeStr[6] = (char) (0x30 + divResult.rem);
+
+    dtDay     = dt.Day;
+    divResult = DIV(dtDay,10);
+    dateTimeStr[8] = (char) (0x30 + divResult.quot);
+    dateTimeStr[9] = (char) (0x30 + divResult.rem);
+
+    dtHour    = dt.Hour;
+    divResult = DIV(dtHour,10);
+    dateTimeStr[11] = (char) (0x30 + divResult.quot);
+    dateTimeStr[12] = (char) (0x30 + divResult.rem);
+
+    dtMin     = dt.Minute;
+    divResult = DIV(dtMin,10);
+    dateTimeStr[14] = (char) (0x30 + divResult.quot);
+    dateTimeStr[15] = (char) (0x30 + divResult.rem);
+
+    dtSec     = dt.Second;
+    divResult = DIV(dtSec,10);
+    dateTimeStr[17] = (char) (0x30 + divResult.quot);
+    dateTimeStr[18] = (char) (0x30 + divResult.rem);
+
+    dtmSec    = dt.Millisecond;
+    divResult = DIV(dtmSec, 100);
+    dateTimeStr[20] = (char) (0x30 + divResult.quot);
+    divResult = DIV(divResult.rem, 10);
+    dateTimeStr[21] = (char) (0x30 + divResult.quot);
+    dateTimeStr[22] = (char) (0x30 + divResult.rem);
+
+    return(true);
+  }
+
+  bool LoopCheck::getDateTime(lcDateTime *dt)
+  {
+    dt->Year        = dtYear;
+    dt->Month       = dtMonth;
+    dt->Day         = dtDay;
+    dt->Hour        = dtHour;
+    dt->Minute      = dtMin;
+    dt->Second      = dtSec;
+    dt->Millisecond = dtmSec;
+    return(true);
+  }
+
+  const char * LoopCheck::refDateTime()
+  {
+    return(dateTimeStr);
+  }
+
+  unsigned long LoopCheck::locMicros()
+  {
+#ifdef smnSimLinux
+    struct timespec clockTime;
+    unsigned long retv;
+
+    clock_gettime(CLOCK_MONOTONIC, &clockTime);
+    retv = clockTime.tv_nsec / 1000;
+    return(retv);
+#endif
+
+#ifdef smnSimWindows
+    LARGE_INTEGER countValue, frequency, result;
+
+    QueryPerformanceCounter(&countValue);
+    QueryPerformanceFrequency(&frequency);
+
+    result.QuadPart = (countValue.QuadPart * 1000000) / frequency.QuadPart;
+    return((unsigned long) result.QuadPart);
+#endif
+
+#ifdef smnSloeber
+    return(micros());
+#endif
+  }
+
+#ifdef smnESP8266
+  div_t LoopCheck::locDiv(int numer, int denom)
+  {
+    div_t retv;
+
+    retv.quot = numer / denom;
+    retv.rem  = numer % denom;
+
+    return(retv);
+  }
+#endif
+
+  void LoopCheck::startTimeMeasure()
+  {
+    measureTimeSet = SYSMICSEC;
+  }
+
+  unsigned long LoopCheck::getTimeMeasure()
+  {
+    return(SYSMICSEC - measureTimeSet);
+  }
+
+  unsigned long LoopCheck::getRuntime()
+  {
+    return(measureRuntime);
+  }
+
+  void LoopCheck::hexAsc(char * dest, byte val)
+  {
+    char cv;
+
+    cv = val >> 4;
+    if(cv < 10)
+      cv += 0x30;
+    else
+      cv += 0x37;
+    dest[0] = cv;
+
+    cv = val & 0x0F;
+    if(cv < 10)
+      cv += 0x30;
+    else
+      cv += 0x37;
+    dest[1] = cv;
+
+    dest[2] = '\0';
+  }
+
+
+  // -------------------------------------------------------------------------
+  // Debug-Funktionen
+  // -------------------------------------------------------------------------
+  //
+#ifdef smnLoopCheckDebug
+
+  void LoopCheck::dbgGetStatistics(char *buffer, int idxItem)
+  {
+    switch(idxItem)
+    {
+      case 0:
+        sprintf(buffer,"lT=%d, lMaxT=%d, lMinT=%d, lAvgT=%d",
+            loopMicros,loopMaxMicros,loopMinMicros,loopAvgMicros);
+        break;
+
+      case 1:
+        sprintf(buffer,"bT=%d, bMaxT=%d, bMinT=%d, bAvgT=%d",
+            backgroundMicros,backgroundMaxMicros,backgroundMinMicros,backgroundAvgMicros);
+        break;
+
+      case 2:
+        sprintf(buffer,"rtAlCnt=%d, lCnt=%d, Scr=%d,%d,%d,%d,%d,%d",
+            periodFailCount, loopCounter, loopScreening[0],loopScreening[1],loopScreening[2],loopScreening[3],loopScreening[4],loopScreening[5]);
+        break;
+    }
+  }
+
+#endif
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/LoopCheck/LoopCheck.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/LoopCheck/LoopCheck.h
new file mode 100644
index 0000000000000000000000000000000000000000..9588f3e56dc3ebe52890b772533793e533aad05d
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/LoopCheck/LoopCheck.h
@@ -0,0 +1,343 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   LoopCheck.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (siehe Wikipedia: Creative Commons)
+//
+#ifndef _LoopCheck_h
+#define _LoopCheck_h
+//-----------------------------------------------------------------------------
+
+#define PeriodMinTime       5000
+// Wenn der Aufrufzyklus der Loop diese Zeit (in Mikrosekunden) überschreitet,
+// dann wird ein Alarmbit gesetzt und ein Alarmzähler inkrementiert
+
+#ifndef LoopScreeningGrades
+  #define LoopScreeningGrades 6
+#endif
+
+#define NrOfTimerTasks      10
+
+#define lcTimer0  0
+#define lcTimer1  1
+#define lcTimer2  2
+#define lcTimer3  3
+#define lcTimer4  4
+#define lcTimer5  5
+#define lcTimer6  6
+#define lcTimer7  7
+#define lcTimer8  8
+#define lcTimer9  9
+
+
+#define NrOfOnceTasks       4
+
+#define lcOnce0  0
+#define lcOnce1  1
+#define lcOnce2  2
+#define lcOnce3  3
+
+#define NrOfToggleTasks     4
+
+#define lcToggle0  0
+#define lcToggle1  1
+#define lcToggle2  2
+#define lcToggle3  3
+
+#define CalcAverageDepth    32
+
+#ifdef UseGithubPath
+  #include "../environment/environment.h"
+#else
+  #include "environment.h"
+#endif
+
+#if defined(smnSimLinux) || defined(smnSimWindows)
+  #include <stdlib.h>
+  #include <string.h>
+  #include <time.h>
+  #define SYSMICSEC locMicros()
+#endif
+
+#ifdef smnSimWindows
+#include <Windows.h>
+#endif
+
+#ifdef smnSloeber
+  #include "Arduino.h"
+  #define SYSMICSEC    micros()
+#endif
+
+#ifdef smnESP8266
+  #define DIV(x,y)    locDiv(x,y)
+#else
+  #define DIV(x,y)    div(x,y)
+#endif
+
+typedef struct _OpHourMeter
+{
+  int   Years;
+  int   Days;
+  int   Hours;
+  int   Minutes;
+  int   Seconds;
+  int   Milliseconds;
+} OpHourMeter;
+
+typedef struct _lcDateTime
+{
+  int   Year;
+  int   Month;
+  int   Day;
+  int   Hour;
+  int   Minute;
+  int   Second;
+  int   Millisecond;
+} lcDateTime;
+
+typedef struct _LoopStatistics
+{
+  unsigned int  loopTime;       // Schleifenzeit in Mikrosekunden
+  unsigned int  loopMaxTime;    // Maximale Schleifenzeit
+  unsigned int  loopMinTime;    // Minimale Schleifenzeit
+  unsigned int  loopAvgTime;    // Mittlere Schleifenzeit
+
+  unsigned int  bgTime;         // Zeit außerhalb der Schleife
+  unsigned int  bgMaxTime;      // Maximale Außenzeit
+  unsigned int  bgMinTime;      // Minimale Außenzeit
+  unsigned int  bgAvgTime;      // Mittlere Außenzeit
+
+  unsigned int  loopPeriod;     // Zeit zwischen loop-Aufrufen
+  unsigned int  maxPeriod;      // Maximale Aufrufdistanz
+  unsigned int  minPeriod;      // Minimale Aufrufdistanz
+  unsigned int  avgPeriod;      // Mittlere Aufrufdistanz
+
+  bool          periodAlarm;    // Aufrufdistanz > PeriodMinTime
+  unsigned int  alarmCount;     // Anzahl der Überschreitungen
+
+  unsigned int  rtScreening[LoopScreeningGrades];
+  // Echtzeitüberwachung (Klassierung der ms Überschreitungen)
+} LoopStatistics;
+
+
+// ---------------------------------------------------------------------------
+// class LoopCheck
+// ---------------------------------------------------------------------------
+//
+class LoopCheck
+{
+  // -------------------------------------------------------------------------
+  // Klassenspezifische Datentypen
+  // -------------------------------------------------------------------------
+  //
+  typedef struct _TimerTask
+  {
+    bool            counterStarted;
+    bool            finished;
+    bool            firstRun;
+    unsigned long   startCount;
+    unsigned long   runCounter;
+    unsigned long   delayCounter;
+    unsigned long   ticks;
+    unsigned int    repCounter;
+  } TimerTask;
+
+  typedef struct _OnceTask
+  {
+    bool            finished;
+    bool            firstRun;
+    unsigned int    waitCounter;
+    unsigned long   startCount;
+  } OnceTask;
+
+private:
+  // -------------------------------------------------------------------------
+  // Lokale Variablen
+  // -------------------------------------------------------------------------
+  //
+  unsigned long checkStartMicros;       // Zeit des ersten Aufrufs von begin()
+
+  unsigned long backgroundMicros;       // Zeit, die außerhalb von loop()
+                                        // verstrichen ist (in Mikrosekunden)
+  unsigned long loopMicros;             // Zeit, die innerhalb von loop()
+                                        // verstrichen ist (in Mikrosekunden)
+  unsigned long loopStartMicros;        // Loop-Startzeit (us seit CPU-Start)
+  unsigned long lastClockMicros;
+  unsigned long lastStartMicros;
+  unsigned long lastRestMicros;
+
+  unsigned long loopEndMicros;          // Loop-Endezeit (us seit CPU-Start)
+  unsigned long clockCycleMicros;       // Abstand zwischen zwei clock ticks
+  unsigned long mainStartMicros;        // Zählerstand bei Programmstart
+
+  unsigned long backgroundMaxMicros;    // Maximale Zeit außerhalb loop()
+  unsigned long backgroundMinMicros;    // Minimale Zeit außerhalb loop()
+  unsigned long backgroundAvgMicros;    // Mittlere Zeit außerhal loop() {32}
+  unsigned long backgroundSumMicros;    // Summe für Mittelwertberechnung
+
+  unsigned long loopMaxMicros;          // Maximale Zeit innerhalb loop()
+  unsigned long loopMinMicros;          // Minimale Zeit innerhalb loop()
+  unsigned long loopAvgMicros;          // Mittlere Zeit innerhalb loop()
+  unsigned long loopSumMicros;          // Summe für Mittelwertberechnung
+
+  unsigned long loopCounter;            // Anzahl der loop()-Durchläufe
+
+  unsigned int  loopScreening[LoopScreeningGrades];
+
+  int           calcAvgCounter;         // Zähler für die Mittelwertbildung
+  bool          firstLoop;              // Spezielle Kennzeichnung erste loop()
+  bool          taskHappened;           // Kennzeichnung: Es lief ein LoopTask
+
+  TimerTask     timerTaskList[NrOfTimerTasks];  // Steuerung der zyklischen
+                                                // Tasks (Timer-Ersatz in loop())
+  OnceTask      onceTaskList[NrOfOnceTasks];
+  bool          toggleTaskList[NrOfToggleTasks];
+
+  int           year;               // Betriebsstundenzähler gesamt
+  int           day;
+  int           hour;
+  int           min;
+  int           sec;
+  int           msec;
+  bool          toggleMilli;
+
+  int           dtYear;             // Zeit / Uhr
+  int           dtMonth;
+  int           dtDay;
+  int           dtHour;
+  int           dtMin;
+  int           dtSec;
+  int           dtmSec;
+  int           febLen;
+  char          dateTimeStr[30];
+
+  unsigned int  periodMicros;           // Zeit zwischen zwei loop-Aufrufen
+  unsigned int  periodMinMicros;
+  unsigned int  periodMaxMicros;
+  unsigned int  periodAvgMicros;
+  unsigned int  periodSumMicros;
+
+  bool          periodFailAlarm;        // periodMicros > Millisekunde
+  unsigned int  periodFailCount;        // Anzahl der Überschreitungen
+
+  unsigned long measureTimeSet;         // Mikrosekunden-Offset Zeitmessung
+
+  unsigned long measureRuntime;         // Laufzeit seit Start in Sekunden
+
+private:
+  // -------------------------------------------------------------------------
+  // Lokale Funktionen
+  // -------------------------------------------------------------------------
+  //
+  void initTasks();
+  void initStatistics();
+  void initClock();
+  unsigned long locMicros();
+#ifdef smnESP8266
+  div_t locDiv(int numer, int denom);
+#endif
+
+public:
+  // -------------------------------------------------------------------------
+  // Konstruktoren und Initialisierungen
+  // -------------------------------------------------------------------------
+  //
+  LoopCheck();
+
+  // -------------------------------------------------------------------------
+  // Anwenderfunktionen
+  // -------------------------------------------------------------------------
+  //
+  void begin();     // Diese Funktion muss am Anfang der Schleife aufgerufen
+                    // werden.
+
+  unsigned int done();  // Diese Funktion kann vor dem Aufruf von end()
+                        // genutzt werden und liefert die Laufzeit bis dahin
+
+  void end();       // Diese Funktion muss am Ende der Schleife aufgerufen
+                    // werden.
+
+  bool timerMicro(int taskIdx, unsigned long repeatTime, unsigned int repetitions);
+  bool timerMicro(int taskIdx, unsigned long repeatTime, unsigned int repetitions, unsigned long delay);
+  // Diese Funktion muss als Bedingung (if) aufgerufen werden, um den
+  // nachfolgenden Block {} mit der Wiederholzeit <repeatTime> auszuführen
+  // Für jede Taskschleife muss ein anderer Index <taskIdx> aus dem Bereich
+  // 0 <= taskIdx < MaxNrOfLoopTasks angegeben werden.
+  // Mit <repetitions> wird angegeben, wie oft der Durchlauf überhaupt erfolgt.
+  // Der Wert 0 gibt an, dass der Task für immer läuft
+
+  bool timerMilli(int taskIdx, unsigned long repeatTime, unsigned int repetitions);
+  bool timerMilli(int taskIdx, unsigned long repeatTime, unsigned int repetitions, unsigned long delay);
+
+  bool once(int taskIdx);
+  // Diese Funktion liefert nur einmal den Wert <true>
+
+  bool once(int taskIdx, unsigned int nrOfLoops);
+  // Diese Funktion liefert nur einmal den Wert <true>
+  // nach Ablauf von nrOfLoops Aufrufen
+
+  bool onceDelayed(int taskIdx, unsigned long delay);
+  // Diese Funktion liefert nur einmal den Wert <true>
+  // nach Ablauf von <delay> Mikrosekunden
+
+  bool toggle(int taskIdx);
+  // Diese Funktion liefert abwechselnd die Werte <true> oder <false>
+
+  unsigned long timerCycle(int taskIdx);
+  // Rückgabe des aktuellen Timerablaufes (startet ab 0).
+
+  bool timerCycleMod(int taskIdx, int modulo);
+  // Liefert alle <modulo> Timerabläufe den Wert <true>
+
+  unsigned long tick(int taskIdx);
+  // Rückgabe des aktuellen Zählwertes in Mikrosekunden
+
+  unsigned long operationTime(OpHourMeter *opHourMeter);
+  // Die Zeit ab Start der CPU
+
+  unsigned long getStatistics(LoopStatistics *statistics);
+  // Statistik über Ablaufzeiten
+
+  void resetStatistics();
+  // Rücksetzen der Statistikdaten
+
+  bool setDateTime(const char *dtStr);
+  // Setzen der Uhr über standardisierten String
+
+  bool setDateTime(lcDateTime dt);
+  // Setzen der Uhr über lokal definierte Struktur
+
+  bool getDateTime(lcDateTime *dt);
+  // Abfragen der Uhr über lokal definierte Struktur
+
+  const char * refDateTime();
+  // Zeiger auf Datum/Uhrzeit holen
+
+  void startTimeMeasure();
+  // Zeitmessung starten
+
+  unsigned long getTimeMeasure();
+  // Zeitmesswert holen
+
+  unsigned long getRuntime();
+  // Laufzeit in Sekunden
+
+  void hexAsc(char * dest, byte val);
+  // Umwandlung byte in Hex-ASCII
+
+  // -------------------------------------------------------------------------
+  // Debug-Funtionen
+  // -------------------------------------------------------------------------
+  //
+
+#ifdef smnLoopCheckDebug
+  void dbgGetStatistics(char *buffer, int idxItem);
+#endif
+
+};
+
+//-----------------------------------------------------------------------------
+#endif
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/LoopCheck/ReadMe.md b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/LoopCheck/ReadMe.md
new file mode 100644
index 0000000000000000000000000000000000000000..7701d9d3347942f8c88807f2f8342bade47ace46
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/LoopCheck/ReadMe.md
@@ -0,0 +1,20 @@
+# Tools for cyclic called procedures
+Arduino Sketches are build on two basic functions. 
+*void setup()* is called once when the CPU is reset and programmers place their initialisation code here.
+*void loop()* is called in an endless loop, i.e. a cyclic entered function. 
+But the cycle time is not determined, it depends on the speed of the CPU and the used resources.
+Many examples for Arduino use the function *delay(milliseconds)* to organise a kind of timing. 
+But this function is really freezing your program for the given number of milliseconds.
+Using a real timer is a good solution, but some CPUs have only less timers 
+and they sometimes are already used for some libraries.
+
+The tools presented with LoopCheck-library give You the features of (many) timers inside *loop()* 
+based on the Arduino-function *micros()* which is called with the macro SYSMICSEC, 
+defined in *LoopCheck.h*.
+
+You will find, that there is another file included: *environment.h*, which you can find here:
+https://github.com/RobertPatzke/homeautomation/blob/developer/libraries/environment/environment.h
+*environment.h* defines the IDE you are using, the CPU and specific development boards. 
+Code is in several parts conditional, depending on the definitions you make in *environment.h*.
+You will see, that this library is not fixed to Arduino, it may be used for any environment
+where cyclic called functions happen.
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/LoopCheck/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/LoopCheck/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..3af69246689b448266e74521f4ff1d792450456f
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/LoopCheck/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "LoopCheck",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/MidiNotes/MidiNotes.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/MidiNotes/MidiNotes.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ec91464f7411b1f25f33b3b536faf3b8dc1e8dcd
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/MidiNotes/MidiNotes.cpp
@@ -0,0 +1,405 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   Midi.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   27. November 2021
+//
+// Der Inhalt dieser Datei sind Festlegungen für steuerbare Midi-Controller.
+//
+//
+
+#include "MidiNotes.h"
+
+// ----------------------------------------------------------------------------
+// Initialisierungen
+// ----------------------------------------------------------------------------
+//
+
+void MidiNotes::begin(int inBpm, NoteDiv inRes, int inMidiCycle, IntrfBuf *inCRB)
+{
+  dword       stdNoteMicroTick;
+
+  crb = inCRB;                        // Zeiger auf Ringpuffer
+  midiCycle = inMidiCycle;            // Zuklus der ZM in Mikrosekunden
+  bpm = inBpm;                        // Beats Per Minute (Metronom)
+  stdNoteMicroTick = 60000000 / bpm;  // Viertelnotenlänge in Mikrosekunden
+  stdNoteCount = stdNoteMicroTick / inMidiCycle;  // "  in Zyklen der ZM
+  stdNoteTick = 60000 / bpm;          // Viertelnotenlänge in Millisekunden
+  minNoteTick = stdNoteTick / inRes;  // Zu erwartende kürzeste Note (ms)
+
+  typeList[nti0].length   = 8 * stdNoteCount;
+  setNoteType(nti0);
+
+  typeList[nti1].length   = 4 * stdNoteCount;
+  setNoteType(nti1);
+
+  typeList[nti2p].length  = 3 * stdNoteCount;
+  setNoteType(nti2p);
+
+  typeList[nti2].length   = 2 * stdNoteCount;
+  setNoteType(nti2);
+
+  typeList[nti4p].length  = stdNoteCount + stdNoteCount / 2;
+  setNoteType(nti4p);
+
+  // Standard-Note = Viertelnote (
+  typeList[nti4].length   = stdNoteCount;
+  setNoteType(nti4);
+
+  typeList[nti8].length   = stdNoteCount / 2;
+  setNoteType(nti8);
+
+  typeList[nti8p].length  = stdNoteCount / 2 + stdNoteCount / 4;
+  setNoteType(nti8p);
+
+  typeList[nti16].length  = stdNoteCount / 4;
+  setNoteType(nti16);
+
+  typeList[nti16p].length = stdNoteCount / 4 + stdNoteCount / 8;
+  setNoteType(nti16p);
+
+  typeList[nti32].length  = stdNoteCount / 8;
+  setNoteType(nti32);
+
+  typeList[nti32p].length = stdNoteCount / 8 + stdNoteCount / 16;
+  setNoteType(nti32p);
+
+  typeList[nti64].length  = stdNoteCount / 16;
+  setNoteType(nti64);
+
+  typeList[nti64p].length = stdNoteCount / 16 + stdNoteCount / 32;
+  setNoteType(nti64p);
+
+  opMode = momIdle;
+  setChannel(1);
+  stopRun = false;
+  stoppedRun = false;
+  next(smInit);
+}
+
+
+// ----------------------------------------------------------------------------
+// Konfiguration
+// ----------------------------------------------------------------------------
+//
+void MidiNotes::setNoteType(NoteTypeIdx nt)
+{
+  NoteTypePtr typePtr;
+
+  typePtr = &typeList[nt];
+  typePtr->attack   = 0;
+  typePtr->decay    = 0;
+  typePtr->sustain  = typePtr->length;
+  typePtr->release  = 0;
+  typePtr->pause    = (typePtr->length * 20) / 100;
+
+  typePtr->deltaAttack    = 0;
+  typePtr->deltaDecay     = 0;
+  typePtr->percentSustain = 70;
+  typePtr->deltaRelease   = 0;
+}
+
+
+void MidiNotes::setNoteType(NoteTypeIdx nt, byte pAttL, byte pDecL, byte pSusL, byte pRelL,
+                            byte pPauL, byte dAtt, byte dDec, byte pSusV, byte dRel)
+{
+  NoteTypePtr typePtr;
+
+  typePtr = &typeList[nt];
+  typePtr->attack   = (typePtr->length * pAttL) / 100;
+  typePtr->decay    = (typePtr->length * pDecL) / 100;
+  typePtr->sustain  = (typePtr->length * pSusL) / 100;
+  typePtr->release  = (typePtr->length * pRelL) / 100;
+  typePtr->pause    = (typePtr->length * pPauL) / 100;
+
+  typePtr->deltaAttack    = dAtt;
+  typePtr->deltaDecay     = dDec;
+  typePtr->percentSustain = pSusV;
+  typePtr->deltaRelease   = dRel;
+}
+
+int MidiNotes::addChordNote(NoteTypeIdx nti, byte val, byte vel)
+{
+  NotePtr notePtr;
+  int     i;
+
+  for(i = 0; i < MaxNrNoteSim; i++)
+  {
+    notePtr = &chord[i];
+    if(notePtr->mode == NoteModeEmpty)
+    {
+      notePtr->mode     = NoteModeRun;
+      notePtr->typeIdx  = nti;
+      notePtr->value    = val;
+      notePtr->veloc    = vel;
+      break;
+    }
+  }
+  return(i);
+}
+
+void MidiNotes::setChannel(int chnVal)
+{
+  if(chnVal < 1) chnVal = 1;
+  if(chnVal > 16) chnVal = 16;
+  chn = chnVal - 1;
+}
+
+// ----------------------------------------------------------------------------
+// Betrieb
+// ----------------------------------------------------------------------------
+//
+void MidiNotes::setOpMode(MidiOpMode mom)
+{
+  opMode = mom;
+}
+
+
+void MidiNotes::setChordNote(int idx, NoteTypeIdx nti, int val, int vel)
+{
+  if(idx < 0) return;
+  if(idx >= MaxNrNoteSim) return;
+
+  if(nti >= 0 && nti < ntiNr)
+    newNote[idx].typeIdx = nti;
+
+  if(val >= 0 && val <= 127)
+    newNote[idx].value = val;
+
+  if(vel >= 0 && vel <= 127)
+    newNote[idx].veloc = vel;
+
+  newNote[idx].newVal = true;
+}
+
+
+// ----------------------------------------------------------------------------
+// Steuerung, Zustandsmaschine
+// ----------------------------------------------------------------------------
+//
+
+void MidiNotes::stop()
+{
+  stopRun = true;
+}
+
+void MidiNotes::resume()
+{
+  stopRun = false;
+  stoppedRun = false;
+}
+
+void MidiNotes::run()
+{
+  runCounter++;
+  if(cycleCnt > 0) cycleCnt--;
+
+  if(nextState != NULL)
+    (this->*nextState)();
+}
+
+void MidiNotes::smInit()
+{
+  next(smIdle);
+}
+
+void MidiNotes::smIdle()
+{
+  switch(opMode)
+  {
+    case momIdle:
+      break;
+
+    case momSequence:
+      next(smNoteOn);
+      break;
+
+    case momRunDelta:
+      break;
+  }
+}
+
+void MidiNotes::smNoteOn()
+{
+  int   i, j, tIdx;
+  bool  doAttack;
+  dword attack, sustain;
+
+  if(stopRun || stoppedRun)         // Unterbrechen/Stoppen des Ablaufs
+  {                                 // vor dem Einschalten einer Note
+    stoppedRun = true;
+    return;
+  }
+
+  if(crb == NULL)                   // Ohne Ausgabepuffer in Wartezustand
+  {
+    next(smIdle);
+    return;
+  }
+
+  doAttack = false;                 // Voreinstellung kein Aufklingen
+
+  // Auslesen der Noten aus dem Akkordspeicher
+  //
+  j = 0;
+  for(i = 0; i < MaxNrNoteSim; i++)
+  {
+    notePtr = &chord[i];
+
+    if(i == 0)  // erste Note
+    {
+      if(notePtr->mode == NoteModeEmpty)  // Wenn die erste Note leer ist
+      {                                   // dann in den Wartezustand
+        next(smIdle);
+        return;
+      }
+      noteSeq[j++] = 0x90 | chn;          // ansonsten startet die Notenfolge **
+    }
+    else        // weitere Noten
+    {
+      if(notePtr->mode == NoteModeEmpty)  // bei leerer Note Schleife beendet
+        break;
+    }
+
+    // Die Noten im Akkordspeicher können durch aktuelle Noten
+    // ersetzt werden.
+
+    if(newNote[i].newVal) // wenn neue Note vorliegt
+    {
+      newNote[i].newVal = false;                  // neue Note quittieren
+      notePtr->typeIdx = newNote[i].typeIdx;      // und Inhalte im
+      notePtr->value = newNote[i].value;          // Akkordspeicher
+      notePtr->veloc = newNote[i].veloc;          // überschreiben
+    }
+
+    noteSeq[j++] = notePtr->value;        // Notenwert in Sequenz eintragen   **
+
+    // Daten für die Note aus der Typenliste holen
+    //
+    tIdx = notePtr->typeIdx;
+    typePtr = &typeList[tIdx];
+    notePtr->cntAttack  = typePtr->attack;    // Aufklingzeit in Zähler
+    notePtr->cntDecay   = typePtr->decay;     // Abklingzeit in Zähler
+    notePtr->cntSustain = typePtr->sustain;   // Klingzeit in Zähler
+    notePtr->cntRelease = typePtr->release;   // Ausklingzeit in Zähler
+    notePtr->cntPause   = typePtr->pause;     // Pausenzeit in Zähler
+
+    if(notePtr->cntAttack != 0)   // Wenn ein Attack-Wert gegeben ist
+    {
+      doAttack = true;            // dann attack markieren
+      attack =                    // und den Wert auf den erste Schritt setzen
+          (typePtr->deltaAttack * notePtr->veloc) / 100;
+      if(attack > 127) attack = 127;
+      noteSeq[j++] = attack;              // Lautstärke in Sequenz eintragen  **
+    }
+    else  // ohne Attack-Wert geht es hier gleich in Sustain weiter
+    {
+      sustain = (typePtr->percentSustain * notePtr->veloc) / 100;
+      if(sustain > 127) sustain = 127;
+      noteSeq[j++] = sustain;             // Lautstärke in Sequenz eintragen  **
+    }
+  }
+
+  crb->putSeq(noteSeq, j);                // Sequenz an Puffer übergeben   *****
+
+  if(doAttack)
+    next(smAttack);
+  else
+    next(smSustain);
+}
+
+void MidiNotes::smAttack()
+{
+
+}
+
+void MidiNotes::smDecay()
+{
+
+}
+
+// TODO
+// Es können noch nicht Noten unterschiedlicher Länge in einem Akkord
+// verarbeitet werden. Bei mehreren eingetragenen Noten würde die
+// kürzeste Note den Ablauf bestimmen.
+
+void MidiNotes::smSustain()
+{
+  int   i;
+  bool  sustFin;
+
+  sustFin = false;
+  for(i = 0; i < MaxNrNoteSim; i++)
+  {
+    notePtr = &chord[i];
+    if(notePtr->mode == NoteModeEmpty)
+      break;
+
+    if(notePtr->cntSustain > 0)     // Die Sustain-Zeit in diesem Zustand verweilen
+      notePtr->cntSustain--;
+    else
+      sustFin = true;
+  }
+
+  if(sustFin)
+    next(smNoteOff);
+}
+
+void MidiNotes::smRelease()
+{
+
+}
+
+void MidiNotes::smNoteOff()
+{
+  int   i,j;
+
+  j = 0;
+  for(i = 0; i < MaxNrNoteSim; i++)   // Alle Noten im Akkord bearbeiten
+  {
+    notePtr = &chord[i];
+    if(notePtr->mode == NoteModeEmpty)
+      break;
+
+    if(i == 0)
+    {
+      noteSeq[j++] = 0x80 | chn;      // Erste Note bestimmt den Befehl AUS    **
+      absPause = notePtr->cntPause;
+    }
+
+    noteSeq[j++] = notePtr->value;    //Erste und weitere Noten liefern Liste  **
+    noteSeq[j++] = 0;
+  }
+
+  crb->putSeq(noteSeq, j);            // Sequenz an Puffer übergeben   *****
+
+  next(smPause);
+}
+
+void MidiNotes::smPause()
+{
+  if(absPause > 0)
+  {
+    absPause--;
+    return;
+  }
+
+  next(smNoteOn);
+}
+
+// ----------------------------------------------------------------------------
+// Debugging
+// ----------------------------------------------------------------------------
+//
+
+
+
+
+
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/MidiNotes/MidiNotes.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/MidiNotes/MidiNotes.h
new file mode 100644
index 0000000000000000000000000000000000000000..0f09fccd7d1a19c3c9171b0cf96c8320f5ad0b44
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/MidiNotes/MidiNotes.h
@@ -0,0 +1,226 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   MidiNotes.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   27. November 2021
+//
+// Der Inhalt dieser Datei sind Festlegungen für steuerbare Midi-Controller
+//
+
+#ifndef MidiNotes_h
+#define MidiNotes_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+#include "ComRingBuf.h"
+
+#define MaxNrNoteSim    4
+#define MaxMidiSeq      (2 * MaxNrNoteSim + 1)
+
+// Definierte Noten
+//
+#define SchlossC    60
+#define Kammerton   69
+
+typedef enum  _NoteDiv
+{
+  nd4   = 1,
+  nd8   = 2,
+  nd16  = 4,
+  nd32  = 8,
+  nd64  = 16
+} NoteDiv;
+
+typedef enum  _MidiOpMode
+{
+  momIdle,
+  momSequence,
+  momRunDelta
+} MidiOpMode;
+
+
+// ----------------------------------------------------------------------------
+//                            M i d i N o t e s
+// ----------------------------------------------------------------------------
+//
+class MidiNotes
+{
+#define next(x) nextState = &MidiNotes::x
+
+public:
+  // -------------------------------------------------------------------------
+  // Öffentliche Datentypen
+  // -------------------------------------------------------------------------
+  //
+  typedef enum _NoteTypeIdx
+  {
+    nti0  = 0,
+    nti1,
+    nti2p,
+    nti2,
+    nti4p,
+    nti4,
+    nti8p,
+    nti8,
+    nti16p,
+    nti16,
+    nti32p,
+    nti32,
+    nti64p,
+    nti64,
+    ntiNr
+  } NoteTypeIdx;
+
+
+private:
+  // -------------------------------------------------------------------------
+  // Private Datentypen
+  // -------------------------------------------------------------------------
+  //
+  typedef void (MidiNotes::*cbVector)(void);
+
+  typedef struct _NoteType
+  {
+    dword   length;
+    dword   attack;
+    dword   decay;
+    dword   sustain;
+    dword   release;
+    dword   pause;
+    byte    deltaAttack;
+    byte    deltaDecay;
+    byte    percentSustain;
+    byte    deltaRelease;
+  } NoteType, *NoteTypePtr;
+
+  typedef struct  _Note
+  {
+    byte      mode;
+    byte      typeIdx;
+    byte      value;
+    byte      veloc;
+    int       state;
+    dword     cntAttack;
+    dword     cntDecay;
+    dword     cntSustain;
+    dword     cntRelease;
+    dword     cntPause;
+  } Note, *NotePtr;
+
+  typedef struct _NewNote
+  {
+    bool      newVal;
+    byte      typeIdx;
+    byte      value;
+    byte      veloc;
+  }NewNote;
+
+#define NoteModeEmpty     0x00
+#define NoteModeRun       0x01
+#define NoteModeDoChange  0x02
+
+  // --------------------------------------------------------------------------
+  // Lokale Daten
+  // --------------------------------------------------------------------------
+  //
+  IntrfBuf    *crb;
+  cbVector    nextState;
+
+  MidiOpMode  opMode;
+
+  dword     runCounter;
+  dword     cycleCnt;
+
+  dword     midiCycle;      // Zustandstakt in Mikrosekunden
+  dword     minNoteTick;    // minimale Notendauer in Millisekunden
+  dword     bpm;            // Beats per Minute (Metronom)
+  dword     stdNoteTick;    // Dauer einer Viertelnote in Millisekunden
+  dword     stdNoteCount;   // Viertelnote in Zyklen der Zustandsmaschine
+
+  Note      chord[MaxNrNoteSim];  // Liste der simultanen Noten (Akkord)
+  NoteType  typeList[ntiNr];      // Liste der Notentypen
+  byte      chn;                  // Aktueller Kanal
+  byte      noteSeq[MaxMidiSeq];  // Lokaler Telegrammaufbau
+
+  NotePtr     notePtr;      // Temporäre Notendaten
+  NoteTypePtr typePtr;      // Temporärer Notentyp
+
+  dword     absPause;       // Pause für den zyklischen Ablauf
+  NewNote   newNote[MaxNrNoteSim];  // Übergabe neuer Noten
+
+  bool      stopRun;           // Anhalten der Midi-Schleife
+  bool      stoppedRun;        // Midi-Schleife angehalten
+
+
+  // --------------------------------------------------------------------------
+  // Lokale Funktionen
+  // --------------------------------------------------------------------------
+  //
+
+  // Zustandsmaschine
+  // -----------------------------
+  void smInit();
+  void smIdle();
+
+  void smNoteOn();
+  void smAttack();
+  void smDecay();
+  void smSustain();
+  void smRelease();
+  void smNoteOff();
+  void smPause();
+
+
+  // --------------------------------------------------------------------------
+  // Inline-Funktionen
+  // --------------------------------------------------------------------------
+  //
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen
+  // --------------------------------------------------------------------------
+  void begin(int inBpm, NoteDiv inRes, int inMidiCycle, IntrfBuf *inCRB);
+
+
+  // --------------------------------------------------------------------------
+  // Konfiguration
+  // --------------------------------------------------------------------------
+  //
+  void  setNoteType(NoteTypeIdx nt);
+
+  void  setNoteType(NoteTypeIdx nt, byte pAttL, byte pDecL, byte pSusL, byte pRelL,
+                    byte dAtt, byte dDec, byte pSusV, byte dRel, byte pPauL);
+
+  int   addChordNote(NoteTypeIdx nti, byte val, byte vel);
+
+  void  setChannel(int chnVal);
+
+  // --------------------------------------------------------------------------
+  // Betrieb
+  // --------------------------------------------------------------------------
+  //
+  void setOpMode(MidiOpMode mom);
+  void setChordNote(int idx, NoteTypeIdx nti, int val, int vel);
+
+  // --------------------------------------------------------------------------
+  // Steuerung, Zustandsmaschine
+  // --------------------------------------------------------------------------
+  //
+  void run();
+  void stop();
+  void resume();
+
+  // --------------------------------------------------------------------------
+  // Debugging
+  // --------------------------------------------------------------------------
+  //
+
+};
+
+
+// ----------------------------------------------------------------------------
+#endif // MidiNotes_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/MidiNotes/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/MidiNotes/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..a95d68926746af5b6e20d30d2a29ea5b513110ad
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/MidiNotes/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "MidiNotes",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/Monitor/Monitor.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/Monitor/Monitor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9c30d24b20011e8d22623c261a295ccfb42fe1ea
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/Monitor/Monitor.cpp
@@ -0,0 +1,1165 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   Monitor.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   15. Mai 2021
+//
+// Der Monitor dient zum direkten Zugriff auf die Ressourcen eines
+// Mikrocontrollers über die serielle Schnittstelle.
+// ACHTUNG!
+// Er ist nicht für die Anwendung des "Serial Monitor" aus der Arduino-IDE
+// bzw. aus Eclipse/Sloeber gedacht, sondern für ein typisches "Terminal".
+// Verwendet wurde bei der Entwicklung unter Linux das GtkTerm.
+//
+
+#include "Monitor.h"
+
+//-----------------------------------------------------------------------------
+// Initialisierungen
+//-----------------------------------------------------------------------------
+
+void Monitor::init(int inMode, int inCpu, LoopCheck *inLcPtr, IntrfTw *inTwPtr)
+{
+  mode          = inMode;
+  cpu           = inCpu;
+  wrIdx         = 0;
+  rdIdx         = 0;
+  blkOut        = false;
+  blkIn         = false;
+  inIdx         = 0;
+  info          = NULL;
+  readOffsAddr  = 0;
+  doReadReg     = false;
+  extraIn       = false;
+  lcPtr         = inLcPtr;
+  twiPtr        = inTwPtr;
+  nrOfChnChar   = '@';
+
+#ifdef smnNANOBLE33
+
+  microTicValPtr  = (dword *) 0x40009548;
+  microTicCapPtr  = (dword *) 0x40009048;
+
+#endif
+
+  nextState =
+      &Monitor::waitEnter;
+}
+
+
+Monitor::Monitor(int inMode, int inCpu)
+{
+  init(inMode, inCpu, NULL, NULL);
+}
+
+Monitor::Monitor(int inMode, int inCpu, LoopCheck *inLcPtr)
+{
+  init(inMode, inCpu, inLcPtr, NULL);
+}
+
+Monitor::Monitor(int inMode, int inCpu, LoopCheck *inLcPtr, IntrfTw *inTwiPtr)
+{
+  init(inMode, inCpu, inLcPtr, inTwiPtr);
+}
+
+//-----------------------------------------------------------------------------
+// Konfiguration, Hilfsfunktionen
+//-----------------------------------------------------------------------------
+//
+int Monitor::putBuf(char c)
+{
+  if(blkIn) return(-1);
+
+  int free = rdIdx - wrIdx - 1;
+  if(free < 0) free += BufSize;
+  if(free < 1) return(0);
+  buffer[wrIdx++] = c;
+  if(wrIdx == BufSize)
+    wrIdx = 0;
+  return(1);
+}
+
+int Monitor::putBuf(char *txt)
+{
+  if(blkIn) return(-1);
+
+  int free = rdIdx - wrIdx - 1;
+  int size = strlen(txt);
+  if(free < 0) free += BufSize;
+  if(free < size) return(0);
+  for(int i = 0; i < size; i++)
+  {
+  buffer[wrIdx++] = txt[i];
+  if(wrIdx == BufSize)
+    wrIdx = 0;
+  }
+  return(size);
+}
+
+char Monitor::getBuf()
+{
+  int num = wrIdx - rdIdx;
+  if(num == 0) return('\0');
+  char c = buffer[rdIdx++];
+  if(rdIdx == BufSize)
+    rdIdx = 0;
+  return(c);
+}
+
+void Monitor::clrBuf()
+{
+  wrIdx = 0;
+  rdIdx = 0;
+}
+
+void Monitor::sendConfig()
+{
+  int           nrChn   = nrOfChnChar & 0x3F;
+  int           bufIdx  = 0;
+  CfgMeasChnPtr cfgPtr;
+
+  // Visualisierungskanäle (Anzahl) anfordern
+  outChar[bufIdx++] = '&';
+  outChar[bufIdx++] = '@';    // Kanalnummer
+  outChar[bufIdx++] = '@';    // Typ
+  outChar[bufIdx++] = nrOfChnChar;
+  outChar[bufIdx++] = '$';
+
+  for(int i = 0; i < nrChn; i++)
+  {
+    cfgPtr = &cfgChnArr[i];
+    outChar[bufIdx++] = '&';
+    outChar[bufIdx++] = (i+1) | 0x40;
+    outChar[bufIdx++] = cfgPtr->type;
+    hexWord(&outChar[bufIdx], cfgPtr->maxVal);
+    bufIdx += 4;
+    hexWord(&outChar[bufIdx], cfgPtr->minVal);
+    bufIdx += 4;
+    if(cfgPtr->name != NULL)
+    {
+      bufIdx += cpyStr(&outChar[bufIdx], cfgPtr->name);
+    }
+    outChar[bufIdx++] = '$';
+  }
+  outChar[bufIdx] = '\0';
+  out(outChar);
+}
+
+//-----------------------------------------------------------------------------
+// Lokale Abläufe
+//-----------------------------------------------------------------------------
+//
+
+volatile dword calcTest1, calcTest2, calcTest3;
+
+void Monitor::waitEnter()
+{
+  char  c;
+
+  busy = false;
+
+  if(!keyHit()) return;
+  c = keyIn();
+  if(c != '\r' && c != '\n')
+  {
+    lastKeyIn = c;
+    return;
+  }
+
+  busy = true;
+
+  blkIn   = true;
+  blkOut  = true;
+  GoPrm
+}
+
+void doSomeCode()
+{
+  for(int i = 0; i < 500; i++)
+  {
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+    calcTest1++;
+  }
+}
+
+volatile dword micTime;
+
+void Monitor::getKey()
+{
+  char  cin,c1,c0;
+
+  if(!keyHit()) return;
+  cin = keyIn();
+  if(mode & modeEcho)
+    out(cin);
+
+  c0 = c1 = '\0';
+
+  if(inIdx == 0)
+    c0 = cin;
+  else if(inIdx == 1)
+  {
+    c0 = inChar[0];
+    c1 = cin;
+  }
+  else if(inIdx == 2)
+  {
+    c0 = inChar[0];
+    c1 = inChar[1];
+  }
+
+  switch(c0)
+  {
+    case '\r':
+      out("\r\n");
+      blkIn   = false;
+      blkOut  = false;
+      GoWt
+      break;
+
+    case 'c':
+    case 'C':
+      if(inIdx == 0)
+      {
+        inChar[inIdx] = cin;
+        inIdx++;
+      }
+      else if(inIdx == 1)
+      {
+
+        if(cin >= '0' && cin <= '9')
+        {
+          inIdx = 0;
+          out('=');
+          int cIdx = cin - 0x30;
+          if(cFlag[cIdx])
+          {
+            cFlag[cIdx] = false;
+            out('0');
+          }
+          else
+          {
+            cFlag[cIdx] = true;
+            out('1');
+          }
+          GoPrm
+        }
+
+        else if(cin == 'f' || cin == 'F')
+        {
+          inChar[inIdx] = cin;
+          inIdx++;
+        }
+      }
+
+      else if(inIdx == 2)
+      {
+        inIdx = 0;
+        if(cin == 'g' || cin == 'G')
+        {
+          sendConfig();
+          GoPrm
+        }
+      }
+      else
+        GoPrm
+      break;
+
+    case 'i':
+    case 'I':
+      if(inIdx == 0)
+      {
+        inChar[inIdx] = cin;
+        inIdx++;
+      }
+      else if(inIdx == 1)
+      {
+        inIdx = 0;
+        out(' ');
+        if(cin == 'a' || cin == 'A')
+          nextState = &Monitor::getTwiAdr;
+        else if(cin == 'l' || cin == 'L')
+          nextState = &Monitor::readTwiList;
+        else if(cin == 'r' || cin == 'R')
+          nextState = &Monitor::readTwiByte;
+        else if(cin == 'w' || cin == 'W')
+          nextState = &Monitor::writeTwiByte;
+      }
+      break;
+
+    case 'r':
+    case 'R':
+      if(inIdx == 0)
+      {
+        inChar[inIdx] = cin;
+        inIdx++;
+      }
+      else if(inIdx == 1)
+      {
+        inIdx = 0;
+        out(' ');
+        if(cin == 'o' || cin == 'O')
+          nextState = &Monitor::getRdOffsAdr;
+        else if(cin == 'r' || cin == 'R')
+          nextState = &Monitor::readRegVal;
+      }
+      break;
+
+    // ------------------------------------------------------------------------
+    case 't':     //                 Zeitmessungen
+    case 'T':     //
+    // ------------------------------------------------------------------------
+      if(inIdx == 0)
+      {
+        inChar[inIdx] = cin;
+        inIdx++;
+      }
+      else if(inIdx == 1)
+      {
+        inIdx = 0;
+        nextState = &Monitor::getTiming;
+        if(cin == 'l' || cin == 'L')
+        {
+          cmdMode1 = 'L';
+        }
+        else if(cin == 'b' || cin == 'B')
+        {
+          cmdMode1 = 'B';
+        }
+        else if(cin == 'c' || cin == 'C')
+        {
+          cmdMode1 = 'C';
+        }
+#ifdef smnNANOBLE33
+        else if(cin == 'p' || cin == 'P')
+        {
+          micTime = micsecs();
+          doSomeCode();
+          micTime = (micsecs() - micTime - 11) / 10;
+          out(' ');
+          out(micTime);
+          GoPrm
+        }
+#endif
+        else if(cin == 'r' || cin == 'R')
+        {
+          if(lcPtr != NULL)
+          {
+            lcPtr->resetStatistics();
+            out(' ');
+          }
+          GoPrm
+        }
+        else if(cin == 't' || cin == 'T')
+        {
+          dword micTime = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          calcTest2 = micros();
+          micTime = (micros() - micTime) / 20;
+          out(' ');
+          out(micTime);
+          GoPrm
+        }
+        else
+        {
+          GoPrm
+        }
+      }
+      break;
+
+    case 'V':
+    case 'v':
+      out(' ');
+      nextState = &Monitor::version;
+      break;
+
+  }
+}
+
+void Monitor::prompt()
+{
+  if(mode & modeNl)
+    out('\n');
+
+  out("\rM>");
+  GoInp
+}
+
+void Monitor::version()
+{
+  out("Monitor: Version 0.1, May 16, 2021");
+  GoPrm
+}
+
+void Monitor::getRdOffsAdr()
+{
+  char  cin;
+  dword val;
+  int   valIdx;
+
+  if(!keyHit()) return;
+  cin = keyIn();
+  readOffsAddr = 0;
+
+  if(cin != '\r')
+  {
+    if(cin >= '0' && cin <= '9')
+      inChar[inIdx] = cin - 0x30;
+    else if(cin >= 'A' && cin <= 'F')
+      inChar[inIdx] = cin - 0x37;
+    else if(cin >= 'a' && cin <= 'f')
+      inChar[inIdx] = cin - 0x57;
+    else
+      return;
+
+    out(cin);
+    if(inIdx < 16)
+      inIdx++;
+    else
+      out((char) 0x08);
+  }
+  else
+  {
+    valIdx = 0;
+    inIdx--;
+    while(inIdx >= 0)
+    {
+      val = inChar[inIdx];
+      readOffsAddr |= val << valIdx * 4;
+      inIdx--;
+      valIdx++;
+    }
+
+    // TEST
+    //out(" = ");
+    //out(readOffsAddr);
+    inIdx = 0;
+    GoPrm
+  }
+}
+
+void Monitor::readRegVal()
+{
+  char  cin;
+  dword val,adr;
+  int   valIdx;
+
+  dword *regPtr;
+
+  if(!keyHit()) return;
+  cin = keyIn();
+  adr = 0;
+
+  if(cin != '\r')
+  {
+    if(cin >= '0' && cin <= '9')
+      inChar[inIdx] = cin - 0x30;
+    else if(cin >= 'A' && cin <= 'F')
+      inChar[inIdx] = cin - 0x37;
+    else if(cin >= 'a' && cin <= 'f')
+      inChar[inIdx] = cin - 0x57;
+    else
+      return;
+
+    out(cin);
+    if(inIdx < 16)
+      inIdx++;
+    else
+      out((char) 0x08);
+  }
+  else
+  {
+    valIdx = 0;
+    inIdx--;
+    while(inIdx >= 0)
+    {
+      val = inChar[inIdx];
+      adr |= val << valIdx * 4;
+      inIdx--;
+      valIdx++;
+    }
+
+    regPtr = (dword *) (readOffsAddr + adr);
+    val = *regPtr;
+
+    out(": ");
+    hexDword(outChar, val);
+    out(outChar);
+    inIdx = 0;
+    GoPrm
+  }
+}
+
+void Monitor::getTiming()
+{
+  LoopStatistics  lStat;
+  unsigned int    maxVal;
+  unsigned int    minVal;
+  unsigned int    avgVal;
+  char  cin;
+
+  if(lcPtr == NULL)
+  {
+    outl("Kein LoopCheck.");
+    GoPrm
+    return;
+  }
+
+  if(!keyHit()) return;
+  cin = keyIn();
+  if(mode & modeEcho)
+    out(cin);
+
+  if(cin == 'r' || cin == 'R')
+  {
+    out(' ');
+    lcPtr->getStatistics(&lStat);
+    if(cmdMode1 == 'L')
+    {
+      maxVal = lStat.loopMaxTime;
+      minVal = lStat.loopMinTime;
+      avgVal = lStat.loopAvgTime;
+    }
+    else if(cmdMode1 == 'B')
+    {
+      maxVal = lStat.bgMaxTime;
+      minVal = lStat.bgMinTime;
+      avgVal = lStat.bgAvgTime;
+    }
+    else if(cmdMode1 == 'C')
+    {
+      maxVal = lStat.maxPeriod;
+      minVal = lStat.minPeriod;
+      avgVal = lStat.avgPeriod;
+    }
+    else
+    {
+      maxVal = 0;
+      minVal = 0;
+      avgVal = 0;
+    }
+
+    out(maxVal); out("/"); out(minVal); out("/"); out(avgVal); out("\r");
+    GoPrm
+  }
+  else if(cin == 'm' || cin == 'M')
+  {
+    lcPtr->resetStatistics();
+    lcPtr->startTimeMeasure();
+    nextState = &Monitor::getLoopMeasure;
+  }
+
+}
+
+void Monitor::getLoopMeasure()
+{
+  LoopStatistics  lStat;
+  unsigned int    maxVal;
+  unsigned int    minVal;
+  unsigned int    avgVal;
+
+  if(lcPtr->getTimeMeasure() < 1000000)
+    return;
+
+  out(' ');
+  lcPtr->getStatistics(&lStat);
+  if(cmdMode1 == 'L')
+  {
+    maxVal = lStat.loopMaxTime;
+    minVal = lStat.loopMinTime;
+    avgVal = lStat.loopAvgTime;
+  }
+  else if(cmdMode1 == 'B')
+  {
+    maxVal = lStat.bgMaxTime;
+    minVal = lStat.bgMinTime;
+    avgVal = lStat.bgAvgTime;
+  }
+  else if(cmdMode1 == 'C')
+  {
+    maxVal = lStat.maxPeriod;
+    minVal = lStat.minPeriod;
+    avgVal = lStat.avgPeriod;
+  }
+  else
+  {
+    maxVal = 0;
+    minVal = 0;
+    avgVal = 0;
+  }
+
+  out(maxVal); out("/"); out(minVal); out("/"); out(avgVal); out("\r");
+  GoPrm
+}
+
+
+void Monitor::getTwiAdr()
+{
+  char  cin;
+
+  if(!keyHit()) return;
+  cin = keyIn();
+  twiAdr = 0;
+
+  if(cin != '\r')
+  {
+    if(cin >= '0' && cin <= '9')
+      inChar[inIdx] = cin - 0x30;
+    else if(cin >= 'A' && cin <= 'F')
+      inChar[inIdx] = cin - 0x37;
+    else if(cin >= 'a' && cin <= 'f')
+      inChar[inIdx] = cin - 0x57;
+    else
+      return;
+
+    out(cin);
+    if(inIdx < 2)
+      inIdx++;
+    else
+      out((char) 0x08);
+  }
+  else
+  {
+    twiAdr = (inChar[0] << 4) | inChar[1];
+
+    out(" = ");
+    out(twiAdr);
+
+    inIdx = 0;
+    GoPrm
+  }
+}
+
+void Monitor::readTwiList()
+{
+  char  cin;
+  int   reg;
+  int   anz;
+
+  char  tmpOut[3];
+
+  TwiStatus twiStatus;
+
+  if(twiPtr == NULL)
+  {
+    out("no Twi");
+    inIdx = 0;
+    GoPrm
+  }
+
+  if(!keyHit()) return;
+  cin = keyIn();
+
+  if(cin != '\r')
+  {
+    if(cin >= '0' && cin <= '9')
+      inChar[inIdx] = cin - 0x30;
+    else if(cin >= 'A' && cin <= 'F')
+      inChar[inIdx] = cin - 0x37;
+    else if(cin >= 'a' && cin <= 'f')
+      inChar[inIdx] = cin - 0x57;
+    else
+      return;
+
+    out(cin);
+    if(inIdx == 1)
+      out(" ");
+
+    if(inIdx < 4)
+      inIdx++;
+    else
+      out((char) 0x08);
+  }
+  else
+  {
+    reg = (inChar[0] << 4) | inChar[1];
+    anz = (inChar[2] << 4) | inChar[3];
+
+    twiByteSeq.len = anz;
+    twiByteSeq.valueRef = byteArray;
+
+    twiStatus = twiPtr->readByteRegSeq(twiAdr, reg, &twiByteSeq);
+
+    out(" [");
+    out((int) twiStatus);
+    out("] ");
+
+    if((int) twiStatus == 128)
+    {
+      for(int i = 0; i < anz; i++)
+      {
+        hexByte(tmpOut, byteArray[i]);
+        out(tmpOut);
+        if(i != (anz-1)) out(':');
+      }
+    }
+
+    inIdx = 0;
+    GoPrm
+  }
+}
+
+void Monitor::readTwiByte()
+{
+  char  cin;
+  int   reg;
+  byte  val;
+
+  if(twiPtr == NULL)
+  {
+    out("no Twi");
+    inIdx = 0;
+    GoPrm
+  }
+
+  if(!keyHit()) return;
+  cin = keyIn();
+
+  if(cin != '\r')
+  {
+    if(cin >= '0' && cin <= '9')
+      inChar[inIdx] = cin - 0x30;
+    else if(cin >= 'A' && cin <= 'F')
+      inChar[inIdx] = cin - 0x37;
+    else if(cin >= 'a' && cin <= 'f')
+      inChar[inIdx] = cin - 0x57;
+    else
+      return;
+
+    out(cin);
+    if(inIdx < 2)
+      inIdx++;
+    else
+      out((char) 0x08);
+  }
+  else
+  {
+    reg = (inChar[0] << 4) | inChar[1];
+
+    val = twiPtr->readByteReg(twiAdr, reg);
+
+    out(" = ");
+    binByte(outChar, val);
+    out(outChar);
+
+    inIdx = 0;
+    GoPrm
+  }
+}
+
+void Monitor::writeTwiByte()
+{
+  char  cin;
+  int   reg;
+  int   val;
+
+  TwiStatus twiStatus;
+
+  if(twiPtr == NULL)
+  {
+    out("no Twi");
+    inIdx = 0;
+    GoPrm
+  }
+
+  if(!keyHit()) return;
+  cin = keyIn();
+
+  if(cin != '\r')
+  {
+    if(cin >= '0' && cin <= '9')
+      inChar[inIdx] = cin - 0x30;
+    else if(cin >= 'A' && cin <= 'F')
+      inChar[inIdx] = cin - 0x37;
+    else if(cin >= 'a' && cin <= 'f')
+      inChar[inIdx] = cin - 0x57;
+    else
+      return;
+
+    out(cin);
+    if(inIdx == 1)
+      out(" ");
+
+    if(inIdx < 4)
+      inIdx++;
+    else
+      out((char) 0x08);
+  }
+  else
+  {
+    reg = (inChar[0] << 4) | inChar[1];
+    val = (inChar[2] << 4) | inChar[3];
+
+    twiStatus = twiPtr->writeByteReg(twiAdr, reg, val);
+
+    out(" : ");
+    out((int) twiStatus);
+
+    inIdx = 0;
+    GoPrm
+  }
+}
+
+//-----------------------------------------------------------------------------
+// Lokale Schnittstelle
+//-----------------------------------------------------------------------------
+//
+void Monitor::print(char c, int eol)
+{
+  putBuf(c);
+  if(eol & eolCR)
+    putBuf('\r');
+  if(eol & eolLF)
+    putBuf('\n');
+}
+
+void Monitor::print(char *txt, int eol)
+{
+  if(txt != NULL)
+    putBuf(txt);
+  if(eol & eolCR)
+    putBuf('\r');
+  if(eol & eolLF)
+    putBuf('\n');
+}
+
+void Monitor::print(byte *hex, int nr, char fill, int eol)
+{
+  if(hex == NULL) return;
+
+  for(int i = 0; i < nr; i++)
+  {
+    hexByte(tmpChar,hex[i]);
+    tmpChar[2] = fill;
+    tmpChar[3] = '\0';
+    putBuf(tmpChar);
+  }
+
+  if(eol & eolCR)
+    putBuf('\r');
+  if(eol & eolLF)
+    putBuf('\n');
+}
+
+void Monitor::print(unsigned int iVal, int eol)
+{
+  char  iBuf[16];
+
+  itoa(iVal, iBuf, 10);
+  putBuf(iBuf);
+  if(eol & eolCR)
+    putBuf('\r');
+  if(eol & eolLF)
+    putBuf('\n');
+}
+
+void Monitor::prints(int iVal, int eol)
+{
+  char  iBuf[16];
+
+  itoa(iVal, iBuf, 10);
+  putBuf(iBuf);
+  if(eol & eolCR)
+    putBuf('\r');
+  if(eol & eolLF)
+    putBuf('\n');
+}
+
+//-----------------------------------------------------------------------------
+// Datenaufbereitung
+//-----------------------------------------------------------------------------
+//
+void Monitor::hexByte(char * dest, byte val)
+{
+  char cv;
+
+  cv = val >> 4;
+  if(cv < 10)
+    cv += 0x30;
+  else
+    cv += 0x37;
+  dest[0] = cv;
+
+  cv = val & 0x0F;
+  if(cv < 10)
+    cv += 0x30;
+  else
+    cv += 0x37;
+  dest[1] = cv;
+
+  dest[2] = '\0';
+}
+
+void Monitor::binByte(char * dest, byte val)
+{
+  byte mask;
+
+  mask = 0x80;
+
+  for(int i = 0; i < 8; i++)
+  {
+    if((val & mask) != 0)
+      dest[i] = '1';
+    else
+      dest[i] = '0';
+    mask >>= 1;
+  }
+
+  dest[8] = '\0';
+}
+
+int   Monitor::cpyStr(char *dest, char *src)
+{
+  int   i = 0;
+
+  while((dest[i] = src[i]) != '\0') i++;
+  return(i);
+}
+
+void Monitor::binDword(char *dest, dword dwVal)
+{
+  int   idx = 0;
+  byte  bVal;
+
+  bVal = dwVal >> 24;
+  binByte(&dest[idx], bVal);
+  idx += 8;
+  dest[idx++] = ' ';
+
+  bVal = dwVal >> 16;
+  binByte(&dest[idx], bVal);
+  idx += 8;
+  dest[idx++] = ' ';
+
+  bVal = dwVal >> 8;
+  binByte(&dest[idx], bVal);
+  idx += 8;
+  dest[idx++] = ' ';
+
+  bVal = dwVal;
+  binByte(&dest[idx], bVal);
+  idx += 8;
+
+  dest[idx] = '\0';
+}
+
+void Monitor::hexDword(char *dest, dword dwVal)
+{
+  int   idx = 0;
+  byte  bVal;
+
+  bVal = dwVal >> 24;
+  hexByte(&dest[idx], bVal);
+  idx += 2;
+
+  bVal = dwVal >> 16;
+  hexByte(&dest[idx], bVal);
+  idx += 2;
+
+  bVal = dwVal >> 8;
+  hexByte(&dest[idx], bVal);
+  idx += 2;
+
+  bVal = dwVal;
+  hexByte(&dest[idx], bVal);
+  idx += 2;
+
+  dest[idx] = '\0';
+}
+
+void Monitor::binWord(char *dest, word wVal)
+{
+  int   idx = 0;
+  byte  bVal;
+
+  bVal = wVal >> 8;
+  binByte(&dest[idx], bVal);
+  idx += 8;
+  dest[idx++] = ' ';
+
+  bVal = wVal;
+  binByte(&dest[idx], bVal);
+  idx += 8;
+
+  dest[idx] = '\0';
+}
+
+void Monitor::hexWord(char *dest, word wVal)
+{
+  int   idx = 0;
+  byte  bVal;
+
+  bVal = wVal >> 8;
+  hexByte(&dest[idx], bVal);
+  idx += 2;
+
+  bVal = wVal;
+  hexByte(&dest[idx], bVal);
+  idx += 2;
+
+  dest[idx] = '\0';
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Anwenderschnittstelle
+//-----------------------------------------------------------------------------
+//
+
+void Monitor::run()
+{
+  char c;
+
+  if(!blkOut)
+  {
+    c = getBuf();
+    if(c != '\0')
+      smnSerial.print(c);
+  }
+  (this->*nextState)();
+}
+
+void Monitor::cprint(char c)
+{
+  print(c, 0);
+}
+
+void Monitor::cprintln(char c)
+{
+  print(c, eolCR | eolLF);
+}
+
+void Monitor::cprintcr(char c)
+{
+  print(c, eolCR);
+}
+
+void Monitor::print(char *txt)
+{
+  print(txt, 0);
+}
+
+void Monitor::println(char *txt)
+{
+  print(txt, eolCR | eolLF);
+}
+
+void Monitor::println()
+{
+  print((char *) NULL, eolCR | eolLF);
+}
+
+void Monitor::printcr(char *txt)
+{
+  print(txt, eolCR);
+}
+
+void Monitor::printcr()
+{
+  print((char *) NULL, eolCR);
+}
+
+void Monitor::print(unsigned int iVal)
+{
+  print(iVal, 0);
+}
+
+void Monitor::prints(int iVal)
+{
+  prints(iVal, 0);
+}
+
+void Monitor::println(unsigned int iVal)
+{
+  print(iVal, eolCR | eolLF);
+}
+
+void Monitor::printcr(unsigned int iVal)
+{
+  print(iVal, eolCR);
+}
+
+void Monitor::print(byte *iVal, int nr, char fill)
+{
+  print(iVal, nr, fill, 0);
+}
+
+void Monitor::printcr(byte *iVal, int nr, char fill)
+{
+  print(iVal, nr, fill, eolCR);
+}
+
+void Monitor::println(byte *iVal, int nr, char fill)
+{
+  print(iVal, nr, fill, eolCR | eolLF);
+}
+
+void Monitor::setInfo(char *txt)
+{
+  info = txt;
+}
+
+void Monitor::config(int inNrOfChn)
+{
+  if(inNrOfChn < 0) return;
+
+  if(inNrOfChn > MaxChn)
+    inNrOfChn = MaxChn;
+
+  nrOfChnChar = inNrOfChn | 0x40;
+}
+
+void Monitor::config(int inChn, char inType, word inMax, word inMin, char *inName)
+{
+  if(inChn < 1) return;
+
+  if(inChn > MaxChn)
+    inChn = MaxChn;
+
+  CfgMeasChnPtr chnPtr = &cfgChnArr[inChn-1];
+  chnPtr->maxVal = inMax;
+  chnPtr->minVal = inMin;
+  chnPtr->name = inName;
+  chnPtr->type = inType;
+}
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/Monitor/Monitor.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/Monitor/Monitor.h
new file mode 100644
index 0000000000000000000000000000000000000000..1ee2bb11830c716703e5489655355c93a039d131
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/Monitor/Monitor.h
@@ -0,0 +1,211 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   Monitor.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   15. Mai 2021
+//
+// Der Monitor dient zum direkten Zugriff auf die Ressourcen eines
+// Mikrocontrollers über die serielle Schnittstelle.
+//
+
+#include  "Arduino.h"
+#include  "environment.h"
+#include  "arduinoDefs.h"
+#include  "LoopCheck.h"
+#include  "IntrfTw.h"
+
+#ifndef Monitor_h
+#define Monitor_h
+// ----------------------------------------------------------------------------
+
+#define keyHit()  smnSerial.available()
+#define keyIn()   smnSerial.read()
+#define out(x)    smnSerial.print(x)
+#define outl(x)   smnSerial.println(x)
+#define GoInp     nextState = &Monitor::getKey;
+#define GoPrm     nextState = &Monitor::prompt;
+#define GoWt      nextState = &Monitor::waitEnter;
+
+#define modeEcho  0x01
+#define modeNl    0x02
+
+#define eolCR     0x01
+#define eolLF     0x02
+#define eolNL     0x03
+
+#define BufSize   512
+#define MaxChn    32
+
+class Monitor
+{
+  // -------------------------------------------------------------------------
+  // class specific data types
+  // -------------------------------------------------------------------------
+  //
+  typedef void (Monitor::*StatePtr)(void);
+  typedef struct _ConfMeasChannel
+  {
+    word    maxVal;
+    word    minVal;
+    char    *name;
+    char    type;
+  } CfgMeasChn, *CfgMeasChnPtr;
+
+private:
+  // --------------------------------------------------------------------------
+  // Lokale Daten
+  // --------------------------------------------------------------------------
+  //
+  int       cpu;
+  int       mode;
+
+#ifdef smnNANOBLE33
+  dword     *microTicValPtr;
+  dword     *microTicCapPtr;
+#endif
+
+  char      buffer[BufSize];
+  int       wrIdx;
+  int       rdIdx;
+  bool      blkOut;
+  bool      blkIn;
+
+  char      inChar[16];
+  char      outChar[128];
+  char      tmpChar[8];
+  int       inIdx;
+  bool      extraIn;
+
+  char      cmdMode1;
+  char      cmdMode2;
+
+  char      *info;
+
+  StatePtr  nextState;
+  LoopCheck *lcPtr;
+
+  IntrfTw     *twiPtr;
+  int         twiAdr;
+  TwiByteSeq  twiByteSeq;
+  byte        byteArray[32];
+
+  dword       readOffsAddr;
+  bool        doReadReg;
+
+  CfgMeasChn  cfgChnArr[MaxChn];
+  char        nrOfChnChar;
+
+  // --------------------------------------------------------------------------
+  // Lokale Funktionen
+  // --------------------------------------------------------------------------
+  //
+  void  init(int mode, int cpu);
+  void  init(int mode, int cpu, LoopCheck *inLcPtr);
+  void  init(int mode, int cpu, LoopCheck *inLcPtr, IntrfTw *inTwPtr);
+
+  void  waitEnter();
+  void  prompt();
+  void  getKey();
+  void  version();
+  void  getRdOffsAdr();
+  void  readRegVal();
+  void  getTiming();
+  void  getLoopMeasure();
+
+  void  getTwiAdr();
+  void  readTwiList();
+  void  readTwiByte();
+  void  writeTwiByte();
+
+  void  print(char c, int eol);
+  void  print(char *txt, int eol);
+  void  print(byte *hex, int nr, char fill, int eol);
+  void  print(unsigned int iVal, int eol);
+  void  prints(int iVal, int eol);
+
+#ifdef smnNANOBLE33
+
+  dword micsecs()
+  {
+    *microTicCapPtr = 1;
+    return(*microTicValPtr);
+  }
+
+#endif
+
+  // --------------------------------------------------------------------------
+  // Datenaufbereitung
+  // --------------------------------------------------------------------------
+  //
+  void hexByte(char *dest, byte val);
+  void binByte(char *dest, byte val);
+  void hexWord(char *dest, word val);
+  void binWord(char *dest, word val);
+  void hexDword(char *dest, dword val);
+  void binDword(char *dest, dword val);
+  int  cpyStr(char *dest, char *src);
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen
+  // --------------------------------------------------------------------------
+
+  Monitor(int mode, int cpu);
+  Monitor(int mode, int cpu, LoopCheck *inLcPtr);
+  Monitor(int mode, int cpu, LoopCheck *inLcPtr, IntrfTw *inTwiPtr);
+
+  // --------------------------------------------------------------------------
+  // Konfiguration und Hilfsfunktionen
+  // --------------------------------------------------------------------------
+  //
+  void  setInfo(char *txt);
+  int   putBuf(char c);
+  int   putBuf(char *txt);
+  char  getBuf();
+  void  clrBuf();
+  void  sendConfig();
+
+
+  // --------------------------------------------------------------------------
+  // Anwenderschnittstelle
+  // --------------------------------------------------------------------------
+  //
+
+  // Funktionen
+  //
+  void run();
+  void cprint(char c);
+  void print(char *txt);
+  void print(unsigned int iVal);
+  void prints(int iVal);
+  void print(byte *iVal, int nr, char fill);
+  void printcr();
+  void cprintcr(char c);
+  void printcr(char *txt);
+  void printcr(unsigned int iVal);
+  void printcr(byte *iVal, int nr, char fill);
+  void println();
+  void cprintln(char c);
+  void println(char *txt);
+  void println(unsigned int iVal);
+  void println(byte *iVal, int nr, char fill);
+
+  void config(int inNrOfChn);
+  void config(int inChn, char inType, word inMax, word inMin, char *inName);
+
+
+  // Zustände (Variablen)
+  //
+  bool  busy;
+  char  lastKeyIn;
+
+  // Steuerbits (Kommandobits)
+  //
+  bool  cFlag[10];
+  };
+
+// ----------------------------------------------------------------------------
+#endif // Monitor_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/Monitor/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/Monitor/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..a9b281138760d16aa71d51ecec1518e22188a30f
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/Monitor/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "Monitor",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SensorLSM9DS1/SensorLSM9DS1.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SensorLSM9DS1/SensorLSM9DS1.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5a57198a5bb3b3c340ffa5a725f7f7c009725d9a
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SensorLSM9DS1/SensorLSM9DS1.cpp
@@ -0,0 +1,875 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   SensorLSM9DS1.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#include "SensorLSM9DS1.h"
+#include <string.h>
+
+// ----------------------------------------------------------------------------
+// Initialisierungen
+// ----------------------------------------------------------------------------
+
+SensorLSM9DS1::SensorLSM9DS1(IntrfTw *refI2C, int inRunCycle)
+{
+  TwiParams twiParams;
+
+  twPtr               = refI2C;
+  runState            = rsInit;
+  twiByteSeq.len      = 12;
+  twiByteSeq.valueRef = byteArray;
+  runCycle            = inRunCycle;
+
+  fullScaleA  = 4;
+  fullScaleG  = 2000;
+  fullScaleM  = 4;
+
+  newValueAG  = false;
+  newValueM   = false;
+
+  avgSetAG    = 0;
+  avgCntAG    = 0;
+  avgSetM     = 0;
+  avgCntM     = 0;
+
+  errorCntAdrNakAG    = 0;
+  errorCntDataNakAG   = 0;
+  errorCntOverAG      = 0;
+
+  errorCntAdrNakM     = 0;
+  errorCntDataNakM    = 0;
+  errorCntOverM       = 0;
+
+  timeOutTwiStatus    = 0;
+  timeOutTwiDataAG    = 0;
+  timeOutTwiDataM     = 0;
+
+  timeOutStatusAG     = 0;
+  toValueStatusAG     = 0;
+  timeOutStatusM      = 0;
+  toValueStatusM      = 0;
+
+  toCntTwiStatusAG  = 0;
+  toCntTwiStatusM   = 0;
+  toCntTwiDataAG    = 0;
+  toCntTwiDataM     = 0;
+  toCntStatusAG     = 0;
+  toCntStatusM      = 0;
+
+  sumA.x = sumA.y = sumA.z = sumG.x = sumG.y = sumG.z = 0;
+  waitCnt = 2;
+
+  refI2C->getParams(&twiParams);
+  switch(twiParams.speed)
+  {
+    case Twi100k:
+      twiCycle = 10;
+      break;
+
+    case Twi250k:
+      twiCycle = 4;
+      break;
+
+    case Twi400k:
+      twiCycle = 2;
+      break;
+  }
+
+  twiStatusCycle  = 40 * twiCycle;
+  twiDataCycleAG  = 160 * twiCycle;
+  twiDataCycleM   = 100 * twiCycle;
+
+  enableMeasAG  = false;
+  enableMeasM   = false;
+
+  runStateCntTotal = 0;
+}
+
+// ----------------------------------------------------------------------------
+// Konfiguration
+// ----------------------------------------------------------------------------
+//
+int SensorLSM9DS1::resetAG()
+{
+  twPtr->writeByteReg(AG_Adr, AG_Ctrl8, 0x05);
+
+  // Get ID
+  return(twPtr->readByteReg(AG_Adr, AG_Id));
+}
+
+int SensorLSM9DS1::resetM()
+{
+  twPtr->writeByteReg(M_Adr, M_Ctrl2, 0x0C);
+
+  // Get ID
+  return(twPtr->readByteReg(M_Adr, M_Id));
+}
+
+int SensorLSM9DS1::reset()
+{
+  int retv;
+
+  twPtr->writeByteReg(AG_Adr, AG_Ctrl8, 0x05);
+  twPtr->writeByteReg(M_Adr, M_Ctrl2, 0x0C);
+
+  retv = twPtr->readByteReg(AG_Adr, AG_Id);
+  retv += twPtr->readByteReg(M_Adr, M_Id);
+  return(retv);
+}
+
+void SensorLSM9DS1::setScanAG(byte scValueAG, byte scValueA, byte scValueG)
+{
+  twPtr->writeByteReg(AG_Adr, AG_Ctrl6, scValueAG | scValueA);
+  twPtr->writeByteReg(AG_Adr, AG_Ctrl1, scValueAG | scValueG);
+}
+
+void SensorLSM9DS1::setScanM(byte scValue1, byte scValue2, byte scValue3, byte scValue4)
+{
+  twPtr->writeByteReg(M_Adr, M_Ctrl1, scValue1);
+  twPtr->writeByteReg(M_Adr, M_Ctrl2, scValue2);
+  twPtr->writeByteReg(M_Adr, M_Ctrl3, scValue3);
+  twPtr->writeByteReg(M_Adr, M_Ctrl4, scValue4);
+}
+
+void SensorLSM9DS1::setTimeOutValues(FreqAG fAG, FreqM fM)
+{
+  int freqA = 1190, freqM = 40000;
+  int cycleA, cycleM;
+
+  enableMeasAG = true;
+
+  switch(fAG)
+  {
+    case FreqAG14_9:
+      freqA = 149;
+      break;
+
+    case FreqAG59_5:
+      freqA = 595;
+      break;
+
+    case FreqAG119:
+      freqA = 1190;
+      break;
+
+    case FreqAG238:
+      freqA = 2380;
+      break;
+
+    case FreqAG476:
+      freqA = 4760;
+      break;
+
+    case FreqAG952:
+      freqA = 9520;
+      break;
+
+    case FreqAG_OFF:
+      freqA = 1190;
+      enableMeasAG = false;
+      break;
+  }
+
+  cycleA = (freqA * runCycle) / 10;     // Zyklusfrequenz
+  toValueStatusAG = 1200000 / cycleA;   // Mikrosekunden + 20% Verlängerung
+
+  // Test
+  //toValueStatusAG += 2;
+
+  enableMeasM = true;
+
+  switch(fM)
+  {
+    case FreqM0_625:
+      freqM = 625;
+      break;
+
+    case FreqM1_25:
+      freqM = 1250;
+      break;
+
+    case FreqM2_5:
+      freqM = 2500;
+      break;
+
+    case FreqM5:
+      freqM = 5000;
+      break;
+
+    case FreqM10:
+      freqM = 10000;
+      break;
+
+    case FreqM20:
+      freqM = 20000;
+      break;
+
+    case FreqM40:
+      freqM = 40000;
+      break;
+
+    case FreqM80:
+      freqM = 80000;
+      break;
+
+    case FreqM_OFF:
+      freqM = 40000;
+      enableMeasM = false;
+      break;
+  }
+
+  cycleM = (freqM * runCycle) / 1000;   // Zyklusfrequenz
+  toValueStatusM = 1200000 / cycleM;    // Mikrosekunden + 20% Verlängerung
+
+  // Test
+  //toValueStatusM += 3;
+
+}
+
+void SensorLSM9DS1::begin(FreqAG freqAG, int avgAG, MaxA maxA, MaxG maxG, FreqM freqM, int avgM, MaxM maxM)
+{
+  setScanAG((byte) freqAG, (byte) maxA | A_LpAuto, (byte) maxG | G_LpHH);
+  setScanM((byte) freqM | Mxy_PmMed | M_TmpOn, (byte) maxM, M_Contin, Mz_PmMed);
+
+  setTimeOutValues(freqAG, freqM);
+
+  if(avgAG == 1)
+  {
+    avgSetAG = 0;
+    avgCntAG = 0;
+  }
+  else
+  {
+    avgSetAG = avgAG;
+    avgCntAG = avgAG;
+  }
+
+  if(avgM == 1)
+  {
+    avgSetM = 0;
+    avgCntM = 0;
+  }
+  else
+  {
+    avgSetM = avgM;
+    avgCntM = avgM;
+  }
+
+  delay(10);
+}
+
+void SensorLSM9DS1::begin()
+{
+  //reset();
+
+  delay(10);
+
+  setScanAG(AG_Odr119, A_Fs4g | A_LpAuto, G_Fs2000 | G_LpHH);
+  setScanM(M_Odr40 | Mxy_PmMed | M_TmpOn, M_Fs4G, M_Contin, Mz_PmMed);
+
+  avgSetAG  = 6;
+  avgCntAG  = 6;
+
+  avgSetM   = 0;
+  avgCntM   = 0;
+
+
+  delay(10);
+}
+
+// ----------------------------------------------------------------------------
+// Steuerfunktionen, gezielte Prozessorzugriffe und Hilfsfunktionen
+// ----------------------------------------------------------------------------
+//
+/*
+void SensorLSM9DS1::run0()
+{
+  switch(runState)
+  {
+    case rsInit:
+      runState = rsScanAGReq;
+      break;
+
+      // ------------ Accel & Gyro ------------
+
+    case rsScanAGReq:
+      twPtr->recByteReg(AG_Adr, AG_Status, &twiByte);
+      runState = rsScanAGChk;
+      break;
+
+    case rsScanAGChk:
+      if(twiByte.twiStatus != TwStFin)
+        break;
+
+      if((twiByte.value & 0x03) == 0)
+      {
+        waitCnt = 2;
+        runState = rsWaitAG;
+        break;
+      }
+
+      twiByteSeq.len = 12;
+      twPtr->recByteRegSeq(AG_Adr, G_Out, &twiByteSeq);
+      runState = rsFetchAG;
+      break;
+
+    case rsWaitAG:
+      if(waitCnt > 0)
+      {
+        waitCnt--;
+        break;
+      }
+      else
+      {
+        runState = rsScanAGReq;
+      }
+      break;
+
+    case rsFetchAG:
+      if(twiByte.twiStatus != TwStFin)
+        break;
+
+      for(int i = 0; i < 12; i++)
+        tmpDataAG.byteArray[i] = byteArray[i];
+
+      if(avgSetAG > 0)
+      {
+        sumA.x += (int) tmpDataAG.valueAG.A.x;
+        sumA.y += (int) tmpDataAG.valueAG.A.y;
+        sumA.z += (int) tmpDataAG.valueAG.A.z;
+        sumG.x += (int) tmpDataAG.valueAG.G.x;
+        sumG.y += (int) tmpDataAG.valueAG.G.y;
+        sumG.z += (int) tmpDataAG.valueAG.G.z;
+        avgCntAG--;
+
+        if(avgCntAG == 0)
+        {
+          rawDataAG.valueAG.A.x = short (sumA.x / avgSetAG);
+          rawDataAG.valueAG.A.y = short (sumA.y / avgSetAG);
+          rawDataAG.valueAG.A.z = short (sumA.z / avgSetAG);
+          rawDataAG.valueAG.G.x = short (sumG.x / avgSetAG);
+          rawDataAG.valueAG.G.y = short (sumG.y / avgSetAG);
+          rawDataAG.valueAG.G.z = short (sumG.z / avgSetAG);
+
+          sumA.x = sumA.y = sumA.z = sumG.x = sumG.y = sumG.z = 0;
+          avgCntAG = avgSetAG;
+
+          newValueAG = true;
+        }
+      }
+      else
+      {
+        rawDataAG.valueAG.A.x = tmpDataAG.valueAG.A.x;
+        rawDataAG.valueAG.A.y = tmpDataAG.valueAG.A.x;
+        rawDataAG.valueAG.A.z = tmpDataAG.valueAG.A.x;
+        rawDataAG.valueAG.G.x = tmpDataAG.valueAG.A.x;
+        rawDataAG.valueAG.G.y = tmpDataAG.valueAG.A.x;
+        rawDataAG.valueAG.G.z = tmpDataAG.valueAG.A.x;
+        newValueAG = true;
+      }
+
+      runState = rsScanAGReq;
+      break;
+  }
+}
+
+void SensorLSM9DS1::run1()
+{
+  switch(runState)
+  {
+    case rsInit:
+      runState = rsScanAGReq;
+      break;
+
+      // ------------ Accel & Gyro ------------
+
+    case rsScanAGReq:
+      twPtr->recByteReg(AG_Adr, AG_Status, &twiByte);
+      runState = rsScanAGChk;
+      break;
+
+    case rsScanAGChk:
+      if(twiByte.twiStatus != TwStFin)
+        break;
+
+      if((twiByte.value & 0x03) == 0)
+      {
+        waitCnt = 2;
+        runState = rsWaitAG;
+        break;
+      }
+
+      twiByteSeq.len = 12;
+      twPtr->recByteRegSeq(AG_Adr, G_Out, &twiByteSeq);
+      runState = rsFetchAG;
+      break;
+
+    case rsWaitAG:
+      if(waitCnt > 0)
+      {
+        waitCnt--;
+        break;
+      }
+      else
+      {
+        runState = rsScanAGReq;
+      }
+      break;
+
+    case rsFetchAG:
+      if(twiByteSeq.twiStatus != TwStFin)
+        break;
+
+      for(int i = 0; i < 12; i++)
+        rawDataAG.byteArray[i] = byteArray[i];
+
+      newValueAG = true;
+
+      runState = rsScanAGReq;
+      break;
+  }
+}
+
+*/
+
+void SensorLSM9DS1::stop()
+{
+  enableMeasAG = false;
+  enableMeasM = false;
+}
+
+void SensorLSM9DS1::resume()
+{
+  enableMeasAG = true;
+  enableMeasM = true;
+}
+
+void SensorLSM9DS1::run()
+{
+  runStateCntTotal++;
+
+  switch(runState)
+  {
+    // ------------------------------------------------------------------------
+    case rsInit:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsInit]++;
+      runState = rsScanAGReq;
+      timeOutStatusAG = toValueStatusAG;
+      timeOutStatusM  = toValueStatusM;
+      break;
+
+      // ------------ Accel & Gyro ------------
+
+    // ------------------------------------------------------------------------
+    case rsScanAGReq:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsScanAGReq]++;
+      if(!enableMeasAG)
+      {
+        runState = rsScanMReq;
+        break;
+      }
+
+      twPtr->recByteReg(AG_Adr, AG_Status, &twiByte);
+      timeOutTwiStatus = twiStatusCycle / runCycle + 1;
+      runState = rsScanAGChk;
+      break;
+
+    // ------------------------------------------------------------------------
+    case rsWaitAG:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsWaitAG]++;
+      break;
+
+    // ------------------------------------------------------------------------
+    case rsScanAGChk:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsScanAGChk]++;
+      if((twiByte.twiStatus != TwStFin) && ((twiByte.twiStatus & TwStError) == 0))
+      {
+        if(timeOutTwiStatus > 0)
+          timeOutTwiStatus--;
+        else
+        {
+          toCntTwiStatusAG++;
+          runState = rsScanAGReq;
+        }
+        break;
+      }
+
+      if((twiByte.twiStatus & TwStError) != 0)
+      {
+        if(twiByte.twiStatus == TwStAdrNak)
+          errorCntAdrNakAG++;
+        else if(twiByte.twiStatus == TwStDataNak)
+          errorCntDataNakAG++;
+        else
+          errorCntOverAG++;
+
+        runState = rsScanAGReq;
+        break;
+      }
+
+      if((twiByte.value & 0x03) == 0)
+      {
+        if(timeOutStatusAG > 0)
+          timeOutStatusAG--;
+        else
+        {
+          timeOutStatusAG = toValueStatusAG;
+          toCntStatusAG++;
+        }
+        runState = rsScanMReq;    // -> Magnet
+        break;
+      }
+
+      timeOutStatusAG = toValueStatusAG;
+      twiByteSeq.len = 12;
+      twPtr->recByteRegSeq(AG_Adr, G_Out, &twiByteSeq);
+      timeOutTwiDataAG = twiDataCycleAG / runCycle + 1;
+      runState = rsFetchAG;
+      break;
+
+    // ------------------------------------------------------------------------
+    case rsFetchAG:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsFetchAG]++;
+      if((twiByteSeq.twiStatus != TwStFin) && ((twiByte.twiStatus & TwStError) == 0))
+      {
+        if(timeOutTwiDataAG > 0)
+        {
+          timeOutTwiDataAG--;
+          break;
+        }
+      }
+
+      if(((twiByteSeq.twiStatus & TwStError) != 0) || (timeOutTwiDataAG == 0))
+      {
+        if(twiByteSeq.twiStatus == TwStAdrNak)
+          errorCntAdrNakAG++;
+        else if(twiByteSeq.twiStatus == TwStDataNak)
+          errorCntDataNakAG++;
+        else if(twiByteSeq.twiStatus == TwStOverrun)
+          errorCntOverAG++;
+        else
+          toCntTwiDataAG++;
+
+        twiByteSeq.len = 12;
+        twPtr->recByteRegSeq(AG_Adr, G_Out, &twiByteSeq);
+        timeOutTwiDataAG = twiDataCycleAG / runCycle + 1;
+        break;
+      }
+
+      for(int i = 0; i < 12; i++)
+        comDataAG.byteArray[i] = byteArray[i];
+
+      if(avgSetAG > 0)
+      {
+        sumA.x += comDataAG.valueAG.A.x;
+        sumA.y += comDataAG.valueAG.A.y;
+        sumA.z += comDataAG.valueAG.A.z;
+        sumG.x += comDataAG.valueAG.G.x;
+        sumG.y += comDataAG.valueAG.G.y;
+        sumG.z += comDataAG.valueAG.G.z;
+        avgCntAG--;
+
+        if(avgCntAG == 0)
+        {
+          rawDataAG.valueAG.A.x = sumA.x / avgSetAG;
+          rawDataAG.valueAG.A.y = sumA.y / avgSetAG;
+          rawDataAG.valueAG.A.z = sumA.z / avgSetAG;
+          rawDataAG.valueAG.G.x = sumG.x / avgSetAG;
+          rawDataAG.valueAG.G.y = sumG.y / avgSetAG;
+          rawDataAG.valueAG.G.z = sumG.z / avgSetAG;
+          sumA.x = sumA.y = sumA.z = sumG.x = sumG.y = sumG.z = 0;
+          avgCntAG = avgSetAG;
+          newValueAG = true;
+        }
+      }
+      else
+      {
+        rawDataAG.valueAG.A.x = comDataAG.valueAG.A.x;
+        rawDataAG.valueAG.A.y = comDataAG.valueAG.A.y;
+        rawDataAG.valueAG.A.z = comDataAG.valueAG.A.z;
+        rawDataAG.valueAG.G.x = comDataAG.valueAG.G.x;
+        rawDataAG.valueAG.G.y = comDataAG.valueAG.G.y;
+        rawDataAG.valueAG.G.z = comDataAG.valueAG.G.z;
+        newValueAG = true;
+      }
+
+      runState = rsScanAGReq;
+      break;
+
+      // ------------ Magnet ------------
+
+    // ------------------------------------------------------------------------
+    case rsScanMReq:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsScanMReq]++;
+      if(!enableMeasM)
+      {
+        runState = rsScanAGReq;
+        break;
+      }
+
+      twPtr->recByteReg(M_Adr, M_Status, &twiByte);
+      timeOutTwiStatus = twiStatusCycle / runCycle + 1;
+      runState = rsScanMChk;
+      break;
+
+    // ------------------------------------------------------------------------
+    case rsScanMChk:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsScanMChk]++;
+      if((twiByte.twiStatus != TwStFin) && ((twiByte.twiStatus & TwStError) == 0))
+      {
+        if(timeOutTwiStatus > 0)
+          timeOutTwiStatus--;
+        else
+        {
+          toCntTwiStatusM++;
+          runState = rsScanMReq;
+        }
+        break;
+      }
+
+      if((twiByte.twiStatus & TwStError) != 0)
+      {
+        if(twiByte.twiStatus == TwStAdrNak)
+          errorCntAdrNakM++;
+        else if(twiByte.twiStatus == TwStDataNak)
+          errorCntDataNakM++;
+        else
+          errorCntOverM++;
+
+        runState = rsScanAGReq;
+        break;
+      }
+
+      if((twiByte.value & 0x08) == 0)
+      {
+        if(timeOutStatusM > 0)
+          timeOutStatusM--;
+        else
+        {
+          timeOutStatusM = toValueStatusM;
+          toCntStatusM++;
+        }
+        runState = rsScanAGReq;    // -> Accel,Gyro
+        break;
+      }
+
+      timeOutStatusM = toValueStatusM;
+      twiByteSeq.len = 6;
+      twPtr->recByteRegSeq(M_Adr, M_Out, &twiByteSeq);
+      timeOutTwiDataM = twiDataCycleM / runCycle + 1;
+      runState = rsFetchM;
+      break;
+
+    // ------------------------------------------------------------------------
+    case rsFetchM:
+    // ------------------------------------------------------------------------
+      runStateCntArray[rsFetchM]++;
+      if((twiByteSeq.twiStatus != TwStFin) && ((twiByte.twiStatus & TwStError) == 0))
+      {
+        if(timeOutTwiDataM > 0)
+        {
+          timeOutTwiDataM--;
+          break;
+        }
+      }
+
+      if( ((twiByteSeq.twiStatus & TwStError) != 0) || (timeOutTwiDataM == 0) )
+      {
+        if(twiByteSeq.twiStatus == TwStAdrNak)
+          errorCntAdrNakM++;
+        else if(twiByteSeq.twiStatus == TwStDataNak)
+          errorCntDataNakM++;
+        else if(twiByteSeq.twiStatus == TwStOverrun)
+          errorCntOverM++;
+        else
+          toCntTwiDataM++;
+
+        twiByteSeq.len = 6;
+        twPtr->recByteRegSeq(AG_Adr, M_Out, &twiByteSeq);
+        timeOutTwiDataM = twiDataCycleM / runCycle + 1;
+        break;
+      }
+
+      for(int i = 0; i < 6; i++)
+        rawDataM.byteArray[i] = byteArray[i];
+
+      if(avgSetM > 0)
+      {
+        sumM.x += rawDataM.valueM.x;
+        sumM.y += rawDataM.valueM.y;
+        sumM.z += rawDataM.valueM.z;
+        avgCntM--;
+
+        if(avgCntM == 0)
+        {
+          rawDataM.valueM.x = sumM.x / avgSetM;
+          rawDataM.valueM.y = sumM.y / avgSetM;
+          rawDataM.valueM.z = sumM.z / avgSetM;
+          sumM.x = sumM.y = sumM.z = 0;
+          avgCntM = avgSetM;
+          newValueM = true;
+        }
+      }
+      else
+      {
+        newValueM = true;
+      }
+
+      runState = rsScanAGReq;
+      break;
+  }
+}
+
+void  SensorLSM9DS1::syncValuesM()
+{
+  newValueM = false;
+}
+
+bool  SensorLSM9DS1::getValuesM(RawDataMPtr rdptr)
+{
+  if(!newValueM) return(false);
+  for(int i = 0; i < 6; i++)
+    rdptr->byteArray[i] = rawDataM.byteArray[i];
+  newValueM = false;
+  return(true);
+}
+
+bool  SensorLSM9DS1::getValuesM(CalValuePtr calPtr)
+{
+  if(!newValueM) return(false);
+
+  calPtr->x = (float) fullScaleM * (float) rawDataM.valueM.x / (float) 32767;
+  calPtr->y = (float) fullScaleM * (float) rawDataM.valueM.y / (float) 32767;
+  calPtr->z = (float) fullScaleM * (float) rawDataM.valueM.z / (float) 32767;
+
+  newValueM = false;
+  return(true);
+}
+
+void  SensorLSM9DS1::syncValuesAG()
+{
+  newValueAG = false;
+}
+
+bool  SensorLSM9DS1::getValuesAG(RawDataAGPtr rdptr)
+{
+  if(!newValueAG) return(false);
+  for(int i = 0; i < 12; i++)
+    rdptr->byteArray[i] = rawDataAG.byteArray[i];
+  newValueAG = false;
+  return(true);
+}
+
+bool  SensorLSM9DS1::getValuesAG(CalValueAGPtr calPtr)
+{
+  if(!newValueAG) return(false);
+
+  calPtr->G.x = (float) fullScaleG * (float) rawDataAG.valueAG.G.x / (float) 32767;
+  calPtr->G.y = (float) fullScaleG * (float) rawDataAG.valueAG.G.y / (float) 32767;
+  calPtr->G.z = (float) fullScaleG * (float) rawDataAG.valueAG.G.z / (float) 32767;
+
+  calPtr->A.x = (float) fullScaleA * (float) rawDataAG.valueAG.A.x / (float) 32767;
+  calPtr->A.y = (float) fullScaleA * (float) rawDataAG.valueAG.A.y / (float) 32767;
+  calPtr->A.z = (float) fullScaleA * (float) rawDataAG.valueAG.A.z / (float) 32767;
+
+  newValueAG = false;
+  return(true);
+}
+
+bool  SensorLSM9DS1::getAvgValuesAG(CalValueAGPtr calPtr)
+{
+  if(!newValueAG) return(false);
+  newValueAG = false;
+
+  if(avgSetAG > 0)
+  {
+    sumA.x += rawDataAG.valueAG.A.x;
+    sumA.y += rawDataAG.valueAG.A.y;
+    sumA.z += rawDataAG.valueAG.A.z;
+    sumG.x += rawDataAG.valueAG.G.x;
+    sumG.y += rawDataAG.valueAG.G.y;
+    sumG.z += rawDataAG.valueAG.G.z;
+    avgCntAG--;
+
+    if(avgCntAG > 0) return(false);
+
+    calPtr->G.x = (float) fullScaleG * (float) (sumG.x / avgSetAG) / (float) 32767;
+    calPtr->G.y = (float) fullScaleG * (float) (sumG.y / avgSetAG) / (float) 32767;
+    calPtr->G.z = (float) fullScaleG * (float) (sumG.z / avgSetAG) / (float) 32767;
+
+    calPtr->A.x = (float) fullScaleA * (float) (sumA.x / avgSetAG) / (float) 32767;
+    calPtr->A.y = (float) fullScaleA * (float) (sumA.y / avgSetAG) / (float) 32767;
+    calPtr->A.z = (float) fullScaleA * (float) (sumA.z / avgSetAG) / (float) 32767;
+
+    avgCntAG = avgSetAG;
+    return(true);
+  }
+
+  calPtr->G.x = (float) fullScaleG * (float) rawDataAG.valueAG.G.x / (float) 32767;
+  calPtr->G.y = (float) fullScaleG * (float) rawDataAG.valueAG.G.y / (float) 32767;
+  calPtr->G.z = (float) fullScaleG * (float) rawDataAG.valueAG.G.z / (float) 32767;
+
+  calPtr->A.x = (float) fullScaleA * (float) rawDataAG.valueAG.A.x / (float) 32767;
+  calPtr->A.y = (float) fullScaleA * (float) rawDataAG.valueAG.A.y / (float) 32767;
+  calPtr->A.z = (float) fullScaleA * (float) rawDataAG.valueAG.A.z / (float) 32767;
+
+  return(true);
+}
+
+
+
+
+// ----------------------------------------------------------------------------
+// Ereignisbearbeitung und Interrupts
+// ----------------------------------------------------------------------------
+//
+
+// ----------------------------------------------------------------------------
+//                      D e b u g - H i l f e n
+// ----------------------------------------------------------------------------
+//
+dword SensorLSM9DS1::debGetDword(int code)
+{
+  dword retv;
+
+
+  switch(code)
+  {
+    case 1:
+      retv = toValueStatusAG;
+      break;
+
+    case 2:
+      retv = toValueStatusM;
+      break;
+
+    default:
+      retv = 0;
+      break;
+  }
+  return(retv);
+}
+
+
+dword SensorLSM9DS1::debGetRunState(int code)
+{
+  if(0 <= code < 9)
+    return(runStateCntArray[code]);
+  else
+    return(0);
+}
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SensorLSM9DS1/SensorLSM9DS1.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SensorLSM9DS1/SensorLSM9DS1.h
new file mode 100644
index 0000000000000000000000000000000000000000..63cb831c96688b30a85397927a14a52dcb6a15f2
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SensorLSM9DS1/SensorLSM9DS1.h
@@ -0,0 +1,363 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   SensorLSM9DS1.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#ifndef SENSORLSM9DS1_H
+#define SENSORLSM9DS1_H
+
+#include "Arduino.h"
+#include "arduinoDefs.h"
+#include "IntrfTw.h"
+
+// ----------------------------------------------------------------------------
+
+// ------------------------ Acceleration and Gyroscope -------
+#define AG_Adr    0x6B
+// ------------------------ Acceleration and Gyroscope -------
+#define AG_Id     0x0F
+#define AG_Ctrl1  0x10
+#define G_Out     0x18
+#define AG_Ctrl6  0x20
+#define AG_Ctrl8  0x22
+#define AG_Status 0x27
+
+#define AG_Rate(x)      (x << 5)
+#define AG_Odr14_9      0x20
+#define AG_Odr59_5      0x40
+#define AG_Odr119       0x60
+#define AG_Odr238       0x80
+#define AG_Odr476       0xA0
+#define AG_Odr952       0xC0
+
+typedef enum _FreqAG
+{
+  FreqAG_OFF  = 0xFF,
+  FreqAG14_9  = AG_Odr14_9,
+  FreqAG59_5  = AG_Odr59_5,
+  FreqAG119   = AG_Odr119,
+  FreqAG238   = AG_Odr238,
+  FreqAG476   = AG_Odr476,
+  FreqAG952   = AG_Odr952
+} FreqAG;
+
+#define AG_FullScale(x) (x << 3)
+#define A_Fs2g          0x00
+#define A_Fs4g          0x10
+#define A_Fs8g          0x18
+#define A_Fs16g         0x08
+#define G_Fs245         0x00
+#define G_Fs2000        0x18
+#define G_Fs500         0x08
+
+typedef enum _MaxA
+{
+  MaxAcc2g    = A_Fs2g,
+  MaxAcc4g    = A_Fs4g,
+  MaxAcc8g    = A_Fs8g,
+  MaxAcc16g   = A_Fs16g
+} MaxA;
+
+typedef enum _MaxG
+{
+  MaxGyro245dps   = G_Fs245,
+  MaxGyro500dps   = G_Fs500,
+  MaxGyro2000dps  = G_Fs2000
+} MaxG;
+
+#define AG_LowPass(x)   (x)
+#define A_LpAuto        0x00
+#define A_Lp50          0x07
+#define A_Lp105         0x06
+#define A_Lp211         0x05
+#define A_Lp408         0x04
+#define G_LpLL          0x00
+#define G_LpLH          0x01
+#define G_LpHL          0x02
+#define G_LpHH          0x03
+
+// ------------------------ Magnetic Field -------
+#define M_Adr     0x1E
+// ------------------------ Magnetic Field -------
+#define M_Id      0x0F
+#define M_Ctrl1   0x20
+#define M_Ctrl2   0x21
+#define M_Ctrl3   0x22
+#define M_Ctrl4   0x23
+#define M_Ctrl5   0x24
+#define M_Status  0x27
+#define M_Out     0x28
+
+// Control 1
+#define M_Rate(x)       (x << 2)
+#define M_Odr0_625      0x00
+#define M_Odr1_25       0x04
+#define M_Odr2_5        0x08
+#define M_Odr5          0x0C
+#define M_Odr10         0x10
+#define M_Odr20         0x14
+#define M_Odr40         0x18
+#define M_Odr80         0x1C
+
+typedef enum _FreqM
+{
+  FreqM_OFF   = 0xFF,
+  FreqM0_625  = M_Odr0_625,
+  FreqM1_25   = M_Odr1_25,
+  FreqM2_5    = M_Odr2_5,
+  FreqM5      = M_Odr5,
+  FreqM10     = M_Odr10,
+  FreqM20     = M_Odr20,
+  FreqM40     = M_Odr40,
+  FreqM80     = M_Odr80
+} FreqM;
+
+#define M_Temp(x)       (x << 7)
+#define M_TmpOn         0x80
+#define M_TmpOff        0x00
+
+#define Mxy_Power(x)    (x << 6)
+#define Mxy_PmLow       0x00
+#define Mxy_PmMed       0x20
+#define Mxy_PmHigh      0x40
+#define Mxy_PmUhigh     0x60
+
+// Control 2
+#define M_FullScale(x)  (x << 5)
+#define M_Fs4G          0x00
+#define M_Fs8G          0x20
+#define M_Fs12G         0x40
+#define M_Fs16G         0x60
+
+typedef enum _MaxM
+{
+  MaxMag4G      = M_Fs4G,
+  MaxMag8G      = M_Fs8G,
+  MaxMag12G     = M_Fs12G,
+  MaxMag16G     = M_Fs16G
+} MaxM;
+
+
+// Control 3
+#define M_OpMode(x)     (x)
+#define M_Contin        0x00
+#define M_Single        0x01
+#define M_Down          0x10
+
+// Control 4
+#define Mz_Power(x)    (x << 2)
+#define Mz_PmLow       0x00
+#define Mz_PmMed       0x04
+#define Mz_PmHigh      0x08
+#define Mz_PmUhigh     0x0C
+
+
+typedef enum _RunState
+{
+  rsInit,
+  rsScanAGReq,
+  rsWaitAG,
+  rsScanAGChk,
+  rsFetchAG,
+  rsScanMReq,
+  rsScanMChk,
+  rsFetchM
+} RunState;
+
+#define NrOfRunStates 8
+
+typedef struct _RawValue
+{
+  short int   x;
+  short int   y;
+  short int   z;
+} RawValue;
+
+typedef struct _SumValue
+{
+  int   x;
+  int   y;
+  int   z;
+} SumValue, *SumValuePtr;
+
+typedef struct _RawValueAG
+{
+  RawValue  G;
+  RawValue  A;
+} RawValueAG;
+
+typedef union _RawDataAG
+{
+  byte        byteArray[12];
+  RawValueAG  valueAG;
+} RawDataAG, *RawDataAGPtr;
+
+typedef union _RawDataM
+{
+  byte        byteArray[6];
+  RawValue    valueM;
+} RawDataM, *RawDataMPtr;
+
+typedef struct _CalValue
+{
+  float   x;
+  float   y;
+  float   z;
+} CalValue, *CalValuePtr;
+
+typedef struct _CalValueAG
+{
+  CalValue  G;
+  CalValue  A;
+} CalValueAG, *CalValueAGPtr;
+
+typedef struct _SensorErrors
+{
+
+} SensorErrors, *SensorErrorsPtr;
+
+class SensorLSM9DS1
+{
+private:
+  // --------------------------------------------------------------------------
+  // Lokale Daten und Funktionen
+  // --------------------------------------------------------------------------
+  //
+  IntrfTw     *twPtr;
+  TwiByte     twiByte;
+  TwiByteSeq  twiByteSeq;
+  byte        byteArray[12];
+
+  bool        enableMeasAG;
+  bool        newValueAG;
+  RawDataAG   rawDataAG;
+  RawDataAG   comDataAG;
+
+  bool        enableMeasM;
+  bool        newValueM;
+  RawDataM    rawDataM;
+
+  int         fullScaleA;
+  int         fullScaleG;
+  int         fullScaleM;
+
+  SumValue    sumA;
+  SumValue    sumG;
+  int         avgSetAG;
+  int         avgCntAG;
+
+  SumValue    sumM;
+  int         avgSetM;
+  int         avgCntM;
+
+  dword       timeOutTwiStatus;
+  dword       timeOutTwiDataAG;
+  dword       timeOutTwiDataM;
+
+  dword       timeOutStatusAG;
+  dword       toValueStatusAG;
+  dword       timeOutStatusM;
+  dword       toValueStatusM;
+
+  int         twiCycle;
+  int         twiStatusCycle;
+  int         twiDataCycleAG;
+  int         twiDataCycleM;
+
+  RunState    runState;
+  int         waitCnt;
+  int         runCycle;
+
+  void setTimeOutValues(FreqAG fAG, FreqM fM);
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen der Basis-Klasse
+  // --------------------------------------------------------------------------
+
+  SensorLSM9DS1(IntrfTw *refI2C, int inRunCycle);
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  int resetAG();
+  int resetM();
+  int reset();
+
+  void setScanAG(byte scValueAG, byte scValueA, byte scValueG);
+  // Messparameter für Accel und Gyro
+  // scValueAG = Abtastrate
+  // scValueA  = Vollausschlag und Tiefpass für Beschleunigung
+  // scValueB  = Vollausschlag und Tiefpass für Gyrometer
+
+  void setScanM(byte scValue1, byte scValue2, byte scValue3, byte scValue4);
+  // Messparameter für Magnetfeld
+  // scValue1 = Abtastrate, Temperaturkompensation und XY-Powermode
+  // scValue2 = Vollausschlag
+  // scValue3 = Betriebsart
+  // scValue4 = Z-Powermode
+
+
+  void begin(FreqAG freqAG, int avgAG, MaxA maxA, MaxG maxG, FreqM freqM, int avgM, MaxM maxM);
+  void begin();
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+  void run();
+  void run0();
+  void run1();
+  void stop();
+  void resume();
+
+  // --------------------------------------------------------------------------
+  // Datenaustausch
+  // --------------------------------------------------------------------------
+  //
+  dword       errorCntOverAG;
+  dword       errorCntAdrNakAG;
+  dword       errorCntDataNakAG;
+
+  dword       errorCntOverM;
+  dword       errorCntAdrNakM;
+  dword       errorCntDataNakM;
+
+  dword       toCntTwiStatusAG;
+  dword       toCntTwiStatusM;
+  dword       toCntTwiDataAG;
+  dword       toCntTwiDataM;
+  dword       toCntStatusAG;
+  dword       toCntStatusM;
+
+  void  syncValuesAG();
+  bool  getValuesAG(RawDataAGPtr rdptr);
+  bool  getValuesAG(CalValueAGPtr calPtr);
+  bool  getAvgValuesAG(CalValueAGPtr calPtr);
+  void  syncValuesM();
+  bool  getValuesM(RawDataMPtr rdptr);
+  bool  getValuesM(CalValuePtr calPtr);
+
+  // ----------------------------------------------------------------------------
+  // Ereignisbearbeitung und Interrupts
+  // ----------------------------------------------------------------------------
+  //
+
+  // ----------------------------------------------------------------------------
+  //                      D e b u g - H i l f e n
+  // ----------------------------------------------------------------------------
+  //
+  dword runStateCntArray[NrOfRunStates];
+  dword runStateCntTotal;
+  dword debGetDword(int code);
+  dword debGetRunState(int code);
+
+};
+
+#endif // SENSORLSM9DS1_H
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SensorLSM9DS1/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SensorLSM9DS1/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..6432e5ccb8930ce40504aa4f03e463f2df00fac0
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SensorLSM9DS1/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "SensorLSM9DS1",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapComDue/SoaapComDue.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapComDue/SoaapComDue.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b24e6f4f90c3ecc1588b5af5dde88b4b922e8ee7
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapComDue/SoaapComDue.cpp
@@ -0,0 +1,212 @@
+//-----------------------------------------------------------------------------
+// Thema:   Steuerung optischer und akustischer Ausgaben für Perfomer
+// Datei:   SoaapComDue.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.hs-hannover.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   01.April 2022
+//
+// Diese Bibliothek (Klassse) enthält diverse Ressourcen zum Zugriff auf den
+// SOAAP-Master über eine serielle Schnittstelle des Arduino DUE.
+//
+
+#include "SoaapComDue.h"
+
+#define next(x) nextState = &SoaapComDue::x
+
+// ----------------------------------------------------------------------------
+// Konstruktoren und Initialisierungen
+// ----------------------------------------------------------------------------
+//
+SoaapComDue::SoaapComDue(USARTClass *serial, SoaapMsg *soaMsg)
+{
+  pCom = serial;
+  pMsg = soaMsg;
+  nextState = &SoaapComDue::runInit;
+
+  nrBytesIn = 0;
+  serInIdx = 0;
+  serInChar = 0;
+  serInCount = 0;
+  slaveAdr = 0;
+  slaveArea = 0;
+  appId = (SoaapApId) 0;
+
+  measIdx = 0;
+  nrMeas = 0;
+  resMeas = 0;
+  slaveIdx = 0;
+  anyNewVal = false;
+}
+
+// --------------------------------------------------------------------------
+// lokale Methoden (Zustandsmaschine)
+// --------------------------------------------------------------------------
+//
+void SoaapComDue::runInit()
+{
+  next(runWaitMsg);
+}
+
+void SoaapComDue::runWaitMsg()
+{
+  nrBytesIn = pCom->available();
+  // Anzahl der über <serial> eingetroffenen Bytes (Zeichen)
+
+  if(nrBytesIn < 1) return;
+  // Beim nächsten Takt wieder in diesen Zustand, wenn kein Zeichen da
+
+  for(serInIdx = 0; serInIdx < nrBytesIn; serInIdx++)
+  {                               // Suchen nach Startzeichen
+    serInChar = pCom->read();
+    if(serInChar < 0x20) break;
+  }
+
+  if(serInIdx == nrBytesIn) return;
+  // Beim nächsten Takt wieder in diesen Zustand, wenn Startzeichen nicht dabei
+
+  slaveArea = serInChar & 0x1F;   // Bereich auskodieren
+
+  serInIdx = 0;     // Index neu setzen für Inhaltszuordnung
+  next(runHeader);
+  // Beim nächsten Takt zum Zustand <runHeader>
+
+}
+
+void SoaapComDue::runHeader()
+{
+    nrBytesIn = pCom->available();
+    // Anzahl der über Serial1 eingetroffenen Bytes (Zeichen)
+
+    if(nrBytesIn < 1) return;
+    // Beim nächsten Takt wieder in diesen Zustand, wenn kein Zeichen da
+
+    serInChar = pCom->read();
+    // einzelnes Zeichen lesen
+
+    if(serInIdx == 0)             // nach der Area folgt die Slaveadresse
+    {
+      slaveAdr = serInChar & 0x1F;               // 1 - 31
+      slaveIdx = slaveAdr;        // vorläufig lineare Adress/Index-Zuordnung
+      serInIdx++;
+      // Beim nächsten Takt wieder in diesen Zustand
+    }
+    else                     // und dann die Anwendungskennung
+    {
+      appId = (SoaapApId) serInChar;
+      measIdx = 0;                      // Index für Messwertunterscheidung
+      serInIdx = 0;                     // Index für Messwertaufbau
+      nrMeas = pMsg->measCnt(appId);   // Anzahl Messwerte im Telegramm
+      resMeas = pMsg->measRes(appId);  // Auflösung der Messwerte in Zeichen
+      next(runValues);
+      // Beim nächsten Takt zum Zustand <runValues>
+    }
+}
+
+void SoaapComDue::runValues()
+{
+  int i;
+
+  do
+  {
+    nrBytesIn = pCom->available();
+    // Anzahl der über Serial1 eingetroffenen Bytes (Zeichen)
+
+    if(nrBytesIn < 1) return;
+    // Beim nächsten Takt wieder in diesen Zustand, wenn kein Zeichen da
+
+    for(i = 0; i < nrBytesIn; i++)
+    {
+      tmpBuffer[serInIdx++] = pCom->read();
+      // einzelnes Zeichen lesen
+
+      if(serInIdx == resMeas) break;
+      // Alle Zeichen vom Messwert da, also raus
+    }
+
+    if(serInIdx < resMeas) return;
+    // Wenn noch nicht ale Zeichen vom Messwert erfasst, dann von vorn
+
+    slValList[slaveIdx].measList[measIdx++] = pMsg->asc2meas(tmpBuffer);
+    // Zeichenkette in Messwert wandeln und speichern
+
+    if(measIdx == nrMeas) break;
+    // Falls mehr als ein Telegramm eingetroffen ist
+    // muss hier ein Ausstieg erfolgen
+
+    serInIdx = 0;   // Nächste Zeichenfolge
+  }
+  while (pCom->available() > 0);
+  // Die Taktzeit der Zustandsmaschine ist größer, als die
+  // Übertragungszeit von einem Zeichen.
+  // Deshalb werden in einem Zustandstakt alle inzwischen eingetroffenen
+  // Zeichen bearbeitet.
+
+  if(measIdx < nrMeas) return;
+  // Im Zustand bleiben, bis alle Messwerte gewandelt sind
+
+  slValList[slaveIdx].newVal = true;
+  anyNewVal = true;
+
+  next(runWaitMsg);
+}
+
+// --------------------------------------------------------------------------
+// lokale Methoden (Hilfsfunktionen)
+// --------------------------------------------------------------------------
+//
+
+// Erster Slave (kleinste Adresse) mit Daten
+//
+int   SoaapComDue::getSlDataAvail()
+{
+  for(int i = 1; i <= ScdNrOfSlaves; i++)
+    if(slValList[i].newVal)
+      return(i);
+  return(0);
+}
+
+// ----------------------------------------------------------------------------
+// Anwenderschnittstelle (Funktionen/Methoden)
+// ----------------------------------------------------------------------------
+//
+
+// (zyklischer) Aufruf zum Ablauf
+//
+void SoaapComDue::run()
+{
+  if(nextState != NULL)
+    (this->*nextState)();
+}
+
+// Daten von irgendeinem Slave verfügbar
+//
+int   SoaapComDue::anyDataAvail()
+{
+  if(!anyNewVal) return(0);
+  else return(getSlDataAvail());
+}
+
+// Daten von bestimmtem Slave verfügbar
+//
+bool  SoaapComDue::slDataAvail(int slAdr)
+{
+  return(slValList[getSlIdxAdr(slAdr)].newVal);
+}
+
+// Daten von einem bestimmten Slave abholen
+//
+int   SoaapComDue::getData(int slAdr, pMeasValues pMeas)
+{
+  int slIdx = getSlIdxAdr(slAdr);
+  for(int i = 0; i < 8; i++)
+    pMeas->defShort[i] = slValList[slIdx].measList[i];
+  slValList[slIdx].newVal = false;
+  anyNewVal = false;
+  for(int i = 1; i <= ScdNrOfSlaves; i++)
+    if(slValList[i].newVal) anyNewVal = true;
+  return(0);
+}
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapComDue/SoaapComDue.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapComDue/SoaapComDue.h
new file mode 100644
index 0000000000000000000000000000000000000000..990faa3710733dedfd4819f254c11b8900f15d65
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapComDue/SoaapComDue.h
@@ -0,0 +1,132 @@
+//-----------------------------------------------------------------------------
+// Thema:   Steuerung optischer und akustischer Ausgaben für Perfomer
+// Datei:   SoaapComDue.h
+// Editor:  Robert Patzke
+// URI/URL: www.hs-hannover.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   01.April 2022
+//
+// Diese Bibliothek (Klassse) enthält diverse Ressourcen zum Zugriff auf den
+// SOAAP-BLE-Master über eine serielle Schnittstelle des Arduino DUE.
+//
+
+#ifndef SoaapComDue_h
+#define SoaapComDue_h
+
+#define ScdNrOfSlaves     6
+
+#include "USARTClass.h"
+#include "SoaapMsg.h"
+
+#ifndef byte
+#define byte unsigned char
+#endif
+
+
+class SoaapComDue
+{
+public:
+  // --------------------------------------------------------------------------
+  // Konstruktoren und Initialisierungen
+  // --------------------------------------------------------------------------
+  //
+  SoaapComDue(USARTClass *serial, SoaapMsg *soaMsg);
+
+  // --------------------------------------------------------------------------
+  // öffentliche Datentypen
+  // --------------------------------------------------------------------------
+  //
+  typedef union
+  {
+    short   defShort[9];
+    short   maxShort[13];
+    float   maxFloat[6];
+  } MeasValues, *pMeasValues;
+
+private:
+  // --------------------------------------------------------------------------
+  // lokale Datentypen
+  // --------------------------------------------------------------------------
+  //
+
+  typedef void (SoaapComDue::*CbVector)(void);
+  typedef struct
+  {
+    int         slaveArea;
+    int         slaveAdr;
+    SoaapApId   apId;
+    bool        newVal;
+    short       measList[13];
+  } SlaveValues, *pSlaveValues;
+
+private:
+  // --------------------------------------------------------------------------
+  // lokale Variablen
+  // --------------------------------------------------------------------------
+  //
+  USARTClass *pCom;
+  SoaapMsg   *pMsg;
+  CbVector    nextState;
+
+  int         nrBytesIn;          // Antahl aktuell empfangener Zeichen
+  int         serInCount;         // Zähler für empfangene Zeichen
+  int         serInIdx;           // Index für besonderes Zeichen
+  byte        serInChar;          // Einzelnes empfangenes Zeichen
+  int         slaveAdr;           // Adresse des Soaap-Slave
+  int         slaveArea;          // Quellennetzwerk-Info, Bereich, o.ä.
+  SoaapApId   appId;              // Anwendungskennung, Datentyp, o.ä.
+
+  int         measIdx;            // Index für den aktuellen Messwert
+  int         nrMeas;             // Anzahl der Messwerte im Telegramm
+  int         resMeas;            // Auflösung der Messwerte in Zeichen
+  bool        anyNewVal;          // Neuer Wert von beliebigem Slave
+
+  SlaveValues slValList[ScdNrOfSlaves + 1]; // Vorläufige Liste aller Messwerte
+  int         slaveIdx;           // Index für lokale Slaveverwaltung
+
+  byte        tmpBuffer[128];     // Zwischenspeicher für empfangene Zeichen
+
+  // --------------------------------------------------------------------------
+  // Inline-Methoden
+  // --------------------------------------------------------------------------
+  //
+
+  // Index von Slave best. Adresse für Datenliste
+  //
+  int  getSlIdxAdr(int slAdr)
+  {
+    // Zur Zeit sind Index und Slaveadresse identisch (1-6)
+    // Das wir später bei freier Zuordnung angepasst
+    return(slAdr);
+  }
+
+  // --------------------------------------------------------------------------
+  // lokale Methoden (Zustandsmaschine)
+  // --------------------------------------------------------------------------
+  //
+  void runInit();
+  void runWaitMsg();
+  void runHeader();
+  void runValues();
+
+  // --------------------------------------------------------------------------
+  // lokale Methoden (Hilfsfunktionen)
+  // --------------------------------------------------------------------------
+  //
+  int   getSlDataAvail();
+
+public:
+  // --------------------------------------------------------------------------
+  // Anwenderschnittstelle (Funktionen)
+  // --------------------------------------------------------------------------
+  //
+  void run();                     // (zyklischer) Aufruf zum Ablauf
+  int   anyDataAvail();           // Daten von irgendeinem Slave verfügbar
+  bool  slDataAvail(int slAdr);   // Daten von bestimmtem Slave verfügbar
+  int   getData(int slAdr, pMeasValues pMeas);
+  // Daten von einem bestimmten Slave abholen
+
+};
+
+#endif // SoaapComDue_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapComDue/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapComDue/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..d2cf1518adba3b9cb3a57d0eca8963fbceca345c
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapComDue/library.json
@@ -0,0 +1,4 @@
+{
+    "name": "SoaapComDue",
+    "version": "0.0.0+20220804174235"
+  }
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapMsg/SoaapMsg.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapMsg/SoaapMsg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0f3f29704b3b1d5e02528071b0527804940435bd
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapMsg/SoaapMsg.cpp
@@ -0,0 +1,159 @@
+//-----------------------------------------------------------------------------
+// Thema:   Steuerung optischer und akustischer Ausgaben für Perfomer
+// Datei:   SoaapMsg.h
+// Editor:  Robert Patzke
+// URI/URL: www.hs-hannover.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   18. März 2022
+//
+// Diese Bibliothek (Klassse) enthält diverse Ressourcen zur Generierung
+// von Meldungen und Telegrammen, die im Rahmen des SOAAP-Projektes
+// eingesetzt werden.
+//
+
+#include "SoaapMsg.h"
+
+// ----------------------------------------------------------------------------
+// Konstruktoren und Initialisierungen
+// ----------------------------------------------------------------------------
+//
+SoaapMsg::SoaapMsg()
+{
+
+}
+
+// ----------------------------------------------------------------------------
+// Anwenderschnittstelle (Funktionen)
+// ----------------------------------------------------------------------------
+//
+
+// Erstellen eines ASCII-Telegramms zum Übertragen der Messwerte
+//
+int   SoaapMsg::getMsgA(int area, int slvNr, SoaapApId appId, char *dest, byte *meas)
+{
+  int   msgIdx = 0;
+  int   measIdx;
+  int   measLen;
+  byte  measByte;
+  char  measChar;
+
+  dest[msgIdx++]  = (char) (area | 0x10);
+  dest[msgIdx++]  = (char) (slvNr | 0x60);
+  dest[msgIdx++]  = (char) (appId);
+
+  switch(appId)
+  {
+    case saiDefaultMeas:
+      measLen = 18;
+      break;
+
+    case saiDefaultMeasCtrl:
+      measLen = 18;
+      break;
+
+   case saiMaximalMeas:
+      measLen = 26;
+      break;
+  }
+
+  for(measIdx = 0; measIdx < measLen; measIdx++)
+  {
+    measByte = meas[measIdx];
+
+    // Erst das niederwertige Nibble als Hex-Ascii eintragen
+    //
+    measChar = (measByte & 0x0F) | 0x30;
+    if (measChar > 0x39) measChar += 7;
+    dest[msgIdx++] = measChar;
+
+    // dann das höherwertige Nibble
+    //
+    measChar = (measByte >> 4) | 0x30;
+    if (measChar > 0x39) measChar += 7;
+    dest[msgIdx++] = measChar;
+  }
+
+  if(appId == saiDefaultMeasCtrl)
+  {
+    measByte = meas[20];
+
+    // Erst das niederwertige Nibble als Hex-Ascii eintragen
+    //
+    measChar = (measByte & 0x0F) | 0x30;
+    if (measChar > 0x39) measChar += 7;
+    dest[msgIdx++] = measChar;
+
+    // dann das höherwertige Nibble
+    //
+    measChar = (measByte >> 4) | 0x30;
+    if (measChar > 0x39) measChar += 7;
+    dest[msgIdx++] = measChar;
+  }
+
+  dest[msgIdx] = '\0';
+  return(msgIdx);
+}
+
+// Umwandeln eines Messwert aus ASCII-Telegramm in Integer
+//
+short  SoaapMsg::asc2meas(byte *ascList)
+{
+  unsigned short retv;
+
+  retv = getVal(ascList[0]);
+  retv += getVal(ascList[1]) << 4;
+  retv += getVal(ascList[2]) << 8;
+  retv += getVal(ascList[3]) << 12;
+
+  return((short) retv);
+}
+
+// Auflösung der Messwerte in Zeichen (Bytes)
+//
+int   SoaapMsg::measRes(SoaapApId appId)
+{
+  int retv = 0;
+
+  switch(appId)
+  {
+    case saiDefaultMeas:
+      retv = 4;
+      break;
+
+    case saiDefaultMeasCtrl:
+      retv = 4;
+      break;
+
+    case saiMaximalMeas:
+      retv = 4;
+      break;
+  }
+
+  return(retv);
+}
+
+// Anzahl der Messwerte im Telegramm
+//
+int   SoaapMsg::measCnt(SoaapApId appId)
+{
+  int retv = 0;
+
+  switch(appId)
+  {
+    case saiDefaultMeas:
+      retv = 9;
+      break;
+
+    case saiDefaultMeasCtrl:
+      retv = 9;
+      break;
+
+    case saiMaximalMeas:
+      retv = 13;
+      break;
+  }
+
+  return(retv);
+}
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapMsg/SoaapMsg.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapMsg/SoaapMsg.h
new file mode 100644
index 0000000000000000000000000000000000000000..20df5732381c353181e02ce74cf3cf193789c5a0
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapMsg/SoaapMsg.h
@@ -0,0 +1,73 @@
+//-----------------------------------------------------------------------------
+// Thema:   Steuerung optischer und akustischer Ausgaben für Perfomer
+// Datei:   SoaapMsg.h
+// Editor:  Robert Patzke
+// URI/URL: www.hs-hannover.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   18. März 2022
+//
+// Diese Bibliothek (Klassse) enthält diverse Ressourcen zur Generierung
+// von Meldungen und Telegrammen, die im Rahmen des SOAAP-Projektes
+// eingesetzt werden.
+//
+
+#ifndef SoaapMsg_h
+#define SoaapMsg_h
+
+#include "arduinoDefs.h"
+
+typedef enum
+{
+  saiDefaultMeas = 0x68,
+  saiMaximalMeas = 0x69,
+  saiDefaultMeasCtrl = 0x6A
+} SoaapApId;
+
+class SoaapMsg
+{
+public:
+  // --------------------------------------------------------------------------
+  // Konstruktoren und Initialisierungen
+  // --------------------------------------------------------------------------
+  //
+  SoaapMsg();
+
+private:
+  // --------------------------------------------------------------------------
+  // lokale Variablen
+  // --------------------------------------------------------------------------
+  //
+
+  // --------------------------------------------------------------------------
+  // Inline-Methoden
+  // --------------------------------------------------------------------------
+  //
+  int getVal(char hexAsc)
+  {
+    if(hexAsc < 0x39) return(hexAsc - 0x30);
+    else return(hexAsc - 0x37);
+  }
+
+
+public:
+  // --------------------------------------------------------------------------
+  // Anwenderschnittstelle (Funktionen)
+  // --------------------------------------------------------------------------
+  //
+
+  int   getMsgA(int area, int slvNr, SoaapApId appId, char *dest, byte *meas);
+  // Erstellen eines ASCII-Telegramms zum Übertragen der Messwerte
+
+  short asc2meas(byte *ascList);
+  // Umwandeln eines Messwert aus ASCII-Telegramm in Integer
+
+  int   measRes(SoaapApId appId);
+  // Auflösung der Messwerte in Zeichen (Bytes)
+
+  int   measCnt(SoaapApId appId);
+  // Anzahl der Messwerte
+
+};
+
+#endif // SoaapMsg_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapMsg/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapMsg/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..86be2b961f0f6f6dae95ed22fadc55f7a1c58826
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/SoaapMsg/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "SoaapMsg",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/StateMachine/StateMachine.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/StateMachine/StateMachine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9c1f78d3b83eabbc30463d78e693b470e345e140
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/StateMachine/StateMachine.cpp
@@ -0,0 +1,480 @@
+// ---------------------------------------------------------------------------
+// File:        StateMachine.cpp
+// Editors:     Robert Patzke,
+// Start:       07. February 2018
+// Last change: 22. February 2021
+// URI/URL:     www.mfp-portal.de, homeautomation.x-api.de
+// Licence:     Creative Commons CC-BY-SA
+// ---------------------------------------------------------------------------
+//
+
+#include "StateMachine.h"
+
+// ---------------------------------------------------------------------------
+// Constructors and initialisations
+// ---------------------------------------------------------------------------
+//
+
+int  StateMachine::StateMachine_InstCounter;
+
+StateMachine::StateMachine(){;} // @suppress("Class members should be properly initialized")
+
+StateMachine::StateMachine(StatePtr firstState, StatePtr anyState, int cycle)
+{
+  begin(firstState, anyState, cycle);
+}
+
+StateMachine::StateMachine(StatePtr firstState, StatePtr anyState, int cycle, MicsecFuPtr micsecFu)
+{
+  begin(firstState, anyState, cycle, micsecFu);
+}
+
+void StateMachine::begin(StatePtr firstState, StatePtr anyState, int cycle)
+{
+  begin(firstState, anyState, cycle, NULL);
+}
+
+void StateMachine::begin(StatePtr firstState, StatePtr anyState, int cycle, MicsecFuPtr micsecFu)
+{
+  nextState     = firstState;
+  doAlways      = anyState;
+  cycleTime     = cycle;
+  frequency     = 1000 / cycle;
+  delay         = 0;
+  delaySet      = 0;
+  repeatDelay   = false;
+  userStatus    = 0;
+
+  StateMachine_InstCounter++;
+  instNumber    = StateMachine_InstCounter;
+  micsFuPtr     = micsecFu;
+}
+
+// ---------------------------------------------------------------------------
+// run      State enter function, has to be cyclic called
+// ---------------------------------------------------------------------------
+//
+void StateMachine::run()
+{
+  unsigned long startMics = 0, diffMics;
+
+  runCounter++;
+
+  if(timeOutCounter > 0)
+    timeOutCounter--;
+
+  if(timeMeasureOn)
+    timeMeasureCounter++;
+
+  if(doAlways != NULL)
+    doAlways();
+
+  if(delay > 0)
+  {
+    delay--;
+    return;
+  }
+
+  if(repeatDelay)
+    delay = delaySet;
+
+  if(useProgList)
+  {
+    if(progIndex == progEndIdx)
+    {
+      if(loopProgList)
+      {
+        progIndex = 0;
+        nextState = progList[progIndex];
+        progIndex++;
+      }
+      else
+      {
+        useProgList = false;
+        nextState = finProgState;
+      }
+    }
+    else
+    {
+      nextState = progList[progIndex];
+      progIndex++;
+    }
+  }
+
+  if(nextState != NULL)
+  {
+    if(micsFuPtr != NULL)
+      startMics = micsFuPtr();
+
+    nextState();
+
+    if(micsFuPtr != NULL)
+    {
+      diffMics = micsFuPtr() - startMics;
+      curStateRuntime = diffMics;
+
+      if(diffMics > maxStateRuntime[0])
+      {
+        maxStateRuntime[0]  = diffMics;
+        maxRuntimeNumber[0] = curStateNumber;
+      }
+      else if(diffMics > maxStateRuntime[1])
+      {
+        if(curStateNumber != maxRuntimeNumber[0])
+        {
+          maxStateRuntime[1]  = diffMics;
+          maxRuntimeNumber[1] = curStateNumber;
+        }
+      }
+      else if(diffMics > maxStateRuntime[2])
+      {
+        if(curStateNumber != maxRuntimeNumber[1])
+        {
+          maxStateRuntime[2]  = diffMics;
+          maxRuntimeNumber[2] = curStateNumber;
+        }
+      }
+      else if(diffMics > maxStateRuntime[3])
+      {
+        if(curStateNumber != maxRuntimeNumber[2])
+        {
+          maxStateRuntime[3]  = diffMics;
+          maxRuntimeNumber[3] = curStateNumber;
+        }
+      }
+    }
+  }
+  else
+    noStateCounter++;
+
+}
+
+// ---------------------------------------------------------------------------
+// service functions/methods for manipulating the state machine
+// ---------------------------------------------------------------------------
+//
+
+// checking a state for staying (not enter new state)
+//
+bool StateMachine::stayHere()
+{
+  if(repeatState > 0)
+  {
+    repeatState--;
+    if(repeatState == 0)
+    {
+      repeatDelay = false;
+    }
+    return(true);
+  }
+
+  firstEnterToggle = true;
+  return(false);
+}
+
+// setting delay before next state
+//
+void StateMachine::setDelay(int delayTime)
+{
+  delay = (delayTime * frequency) / 1000;
+  repeatDelay = false;
+}
+
+// setting internal speed of the state machine
+// can only be slower than the calling frequency
+//
+void StateMachine::setSpeed(int freq)
+{
+  delaySet      = delay = frequency / freq;
+  repeatDelay   = true;
+}
+
+// setting future state to be called by run
+//
+void StateMachine::enter()
+{
+  if(stayHere()) return;
+
+  pastState = nextState;
+  nextState = futureState;
+}
+
+// setting next state to be called by run
+//
+void StateMachine::enter(StatePtr next)
+{
+  if(stayHere()) return;
+
+  pastState = nextState;
+  nextState = next;
+}
+
+// setting next state with delay (before)
+//
+void StateMachine::enter(StatePtr next, int delayTime)
+{
+  if(stayHere()) return;
+
+  delay = (delayTime * frequency) / 1000;
+  repeatDelay = false;
+
+  pastState = nextState;
+  nextState = next;
+}
+
+// setting next state with repetition
+//
+void StateMachine::enterRep(StatePtr next, int count)
+{
+  if(stayHere()) return;
+
+  repeatState   = count;
+  pastState     = nextState;
+  nextState     = next;
+}
+
+// setting next state with repetition and delay
+//
+void StateMachine::enterRep(StatePtr next, int count, int delayTime)
+{
+  if(stayHere()) return;
+
+  delaySet = delay = (delayTime * frequency) / 1000;
+  repeatDelay = true;
+
+  repeatState   = count;
+  pastState     = nextState;
+  nextState     = next;
+}
+
+// setting state and state after
+//
+void StateMachine::enterVia(StatePtr next, StatePtr then)
+{
+  if(stayHere()) return;
+
+  pastState     = nextState;
+  nextState     = next;
+  futureState   = then;
+}
+
+// calling state
+//
+void StateMachine::call(StatePtr next)
+{
+  if(stayHere()) return;
+
+  pastState     = nextState;
+  futureState   = nextState;
+  nextState     = next;
+}
+
+// setting state and state after with delay
+//
+void StateMachine::enterVia(StatePtr next, StatePtr after, int delayTime)
+{
+  if(stayHere()) return;
+
+  delay = (delayTime * frequency) / 1000;
+  repeatDelay = false;
+
+  pastState     = nextState;
+  nextState     = next;
+  futureState   = after;
+}
+
+// calling state
+//
+void StateMachine::call(StatePtr next, int delayTime)
+{
+  if(stayHere()) return;
+
+  delay = (delayTime * frequency) / 1000;
+  repeatDelay = false;
+
+  pastState     = nextState;
+  futureState   = nextState;
+  nextState     = next;
+}
+
+
+// setting state to state list (program)
+//
+void StateMachine::enterList(StatePtr fin)
+{
+  if(stayHere()) return;
+
+  pastState     = nextState;
+  useProgList   = true;
+  progIndex     = 0;
+  finProgState  = fin;
+}
+
+// setting state to state list (program)and delay
+//
+void StateMachine::enterList(StatePtr fin, int delayTime)
+{
+  if(stayHere()) return;
+
+  delay = (delayTime * frequency) / 1000;
+  repeatDelay = false;
+
+  pastState     = nextState;
+  useProgList   = true;
+  progIndex     = 0;
+  finProgState  = fin;
+}
+
+// setting state to state list (program) at a position
+//
+void StateMachine::enterListAt(StatePtr fin, int index)
+{
+  if(stayHere()) return;
+
+  pastState     = nextState;
+  useProgList   = true;
+  progIndex     = index;
+  finProgState  = fin;
+}
+
+// setting state to state list (program)at a position and delay
+//
+void StateMachine::enterListAt(StatePtr fin, int index, int delayTime)
+{
+  if(stayHere()) return;
+
+  delay = (delayTime * frequency) / 1000;
+  repeatDelay = false;
+
+  pastState     = nextState;
+  useProgList   = true;
+  progIndex     = index;
+  finProgState  = fin;
+}
+
+// detecting the first call of a state
+//
+bool StateMachine::firstEnter()
+{
+  if(!firstEnterToggle)
+    return(false);
+  firstEnterToggle = false;
+  return(true);
+}
+
+// reset first enter detector
+//
+void StateMachine::resetEnter()
+{
+  firstEnterToggle = true;
+}
+
+// counting local cycles
+//
+bool StateMachine::cycle(int cnt)
+{
+  if(callCycleCnt <= 0)
+  {
+    callCycleCnt = cnt;
+    return(true);
+  }
+  callCycleCnt--;
+  return(false);
+}
+//
+bool StateMachine::cycleSec()
+{
+  if(callCycleCnt <= 0)
+  {
+    callCycleCnt = frequency;
+    return(true);
+  }
+  callCycleCnt--;
+  return(false);
+}
+
+// Alternativly returning true and false
+//
+bool StateMachine::toggle()
+{
+  markToggle = !markToggle;
+  return(markToggle);
+}
+
+// Doing only once
+//
+bool StateMachine::oneShot()
+{
+  if(!markOneShot) return(false);
+
+  markOneShot = false;
+  return (true);
+}
+
+void StateMachine::setOneShot()
+{
+  markOneShot = true;
+}
+
+
+// Setable number of returning TRUE
+//
+void  StateMachine::setCondCounter(unsigned int condVal)
+{
+  condCounter = condVal;
+}
+
+bool  StateMachine::condOpen()
+{
+  if(condCounter == 0)
+    return(false);
+  condCounter--;
+  return(true);
+}
+
+// set the time-out value (milliseconds)
+//
+void StateMachine::setTimeOut(int toValue)
+{
+  timeOutCounter = (toValue * frequency) / 1000;
+}
+
+// check the time-out counter
+//
+bool StateMachine::timeOut()
+{
+  if(timeOutCounter > 0)
+    return(false);
+  return(true);
+}
+
+// start time measurement
+//
+void StateMachine::startTimeMeasure()
+{
+  timeMeasureCounter = 0;
+  timeMeasureOn = true;
+}
+
+int StateMachine::getTimeMeasure(bool stop)
+{
+  if(stop)
+    timeMeasureOn = false;
+  return(cycleTime * timeMeasureCounter);
+}
+
+unsigned long StateMachine::getExtTimeMeasure(bool stop)
+{
+  if(stop)
+    timeMeasureOn = false;
+  return(cycleTime * timeMeasureCounter);
+}
+
+// ---------------------------------------------------------------------------
+// Debug and statistic functions/methods
+// ---------------------------------------------------------------------------
+//
+int StateMachine::getDelay()
+{
+  return(delay);
+}
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/StateMachine/StateMachine.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/StateMachine/StateMachine.h
new file mode 100644
index 0000000000000000000000000000000000000000..b5a6ecea89a1a4a81795bf2992e52dd58bb350d2
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/StateMachine/StateMachine.h
@@ -0,0 +1,171 @@
+// ---------------------------------------------------------------------------
+// File:        StateMachine.cpp
+// Editors:     Robert Patzke,
+// Start:       07. February 2018
+// Last change: 07. February 2018
+// URI/URL:     www.mfp-portal.de, homeautomation.x-api.de
+// Licence:     Creative Commons CC-BY-SA
+// ---------------------------------------------------------------------------
+//
+
+#include    "stdlib.h"
+
+
+#ifndef _StateMachine_h
+#define _StateMachine_h
+// ---------------------------------------------------------------------------
+
+#define NrOfSteps       32
+
+#define AUTBREAK(x,y)   { x.enter(y); return; }
+
+// ---------------------------------------------------------------------------
+// class StateMachine
+// ---------------------------------------------------------------------------
+//
+
+class StateMachine
+{
+  // -------------------------------------------------------------------------
+  // class specific data types
+  // -------------------------------------------------------------------------
+  //
+  typedef void (*StatePtr)(void);
+  typedef unsigned long (*MicsecFuPtr)(void);
+
+private:
+  // -------------------------------------------------------------------------
+  // local variables
+  // -------------------------------------------------------------------------
+  //
+  int       delay;              // Setting of delay for next state
+  int       delaySet;           // Setup value for repeated delays
+  bool      repeatDelay;        // If true, every state is delayed (speed set)
+  int       repeatState;        // Controlled repeating a state
+  bool      useVarState;        // breaking the fixed sequence of states by
+                                // using a variable state (in futureState)
+
+  bool        useProgList;            // control progList usage
+  bool        loopProgList;           // looping proglist
+  StatePtr    progList[NrOfSteps];    // dynamically created state run list
+  int         progIndex;              // Program counter (index to next state)
+  int         progEndIdx;             // Index to next empty progList entry
+  StatePtr    finProgState;           // State after using state list
+  MicsecFuPtr micsFuPtr;              // Pointer to micsec function
+
+  bool      firstEnterToggle;       // Is set to true when state changes
+  int       timeOutCounter;         // automatic decremented counter
+
+  bool            timeMeasureOn;          // control of the measurement counter
+  unsigned long   timeMeasureCounter;     // triggered automatic incremented counter
+
+  int       callCycleCnt;           // For cycles inside a state
+  bool      markToggle;             // For bit complements
+  bool      markOneShot;            // for doing only one time
+
+  unsigned int  condCounter;        // Counter for questionable conditions
+
+  // -------------------------------------------------------------------------
+  // local functions/methods
+  // -------------------------------------------------------------------------
+  //
+  bool stayHere();
+
+public:
+  // -------------------------------------------------------------------------
+  // public variables
+  // -------------------------------------------------------------------------
+  //
+  StatePtr  nextState;          // Pointer for indirect calling next state
+  StatePtr  pastState;          // Pointer of the past state
+  StatePtr  futureState;        // Pointer to a future state (see useVarState)
+  StatePtr  doAlways;           // Pointer to a always called state
+  int       cycleTime;          // Cycle time in milliseconds
+  int       frequency;          // Frequency in Hertz (1/s)
+  int       userStatus;         // A number presenting the visible state
+
+  // statistic information
+  //
+  static int   StateMachine_InstCounter;
+
+  unsigned int    noStateCounter;       // Counter for empty running of state machine
+  unsigned int    instNumber;           // Number of this instance
+  unsigned int    runCounter;           // Counter for running states
+  unsigned int    curStateNumber;       // Number of current state
+
+  unsigned long   curStateRuntime;      // run time of latest state
+  unsigned long   maxStateRuntime[4];   // Maximum run time of a state
+  unsigned long   maxRuntimeNumber[4];  // Number of the state of maximum runtime
+
+  // error and debug support
+  //
+  int   userError;
+
+  // -------------------------------------------------------------------------
+  // constructors and initialisations
+  // -------------------------------------------------------------------------
+  //
+  StateMachine();
+  StateMachine(StatePtr firstState, StatePtr anyState, int cycle);
+  void begin(StatePtr firstState, StatePtr anyState, int cycle);
+  StateMachine(StatePtr firstState, StatePtr anyState, int cycle, MicsecFuPtr micsecFu);
+  void begin(StatePtr firstState, StatePtr anyState, int cycle, MicsecFuPtr micsecFu);
+
+  // -------------------------------------------------------------------------
+  // user functions
+  // -------------------------------------------------------------------------
+  //
+  void      run();                                  // has to be cyclic called
+  void      setDelay(int delayTime);                // delay before next state
+  void      setSpeed(int freq);                     // internal run frequency
+
+  void      enter();                                // set next to future state
+  void      enter(StatePtr state);                  // set next state to run
+  void      enter(StatePtr state, int delayTime);   // ... delayed
+
+  void      enterRep(StatePtr state, int count);    // repeat next state
+  void      enterRep(StatePtr state, int count, int delayTime);
+
+  void      call(StatePtr state);                   // set next state and return
+  void      call(StatePtr state, int delayTime);    // ... delayed
+
+  void      enterVia(StatePtr next, StatePtr future);       // next and then
+  void      enterVia(StatePtr next, StatePtr future, int delayTime);
+
+  void      enterList(StatePtr fin);
+  void      enterList(StatePtr fin, int delayTime);
+
+  void      enterListAt(StatePtr fin, int index);
+  void      enterListAt(StatePtr fin, int index, int delayTime);
+
+  bool      firstEnter();       // true only, if the state is first entered
+  void      resetEnter();       // reset first enter mark
+  bool      cycle(int cnt);     // true only, if called <cnt> times
+  bool      cycleSec();         // true only, if a second passed
+  bool      toggle();           // alternating return true and false
+  bool      oneShot();          // only one time true for a call
+  void      setOneShot();       // preparing oneShot to be true
+
+  void      setTimeOut(int toValue);        // Set time-out counter
+  bool      timeOut();                      // Check time-out counter
+  void      startTimeMeasure();             // Start time measurement
+  int       getTimeMeasure(bool stop);      // Time in milliseconds
+
+  unsigned long getExtTimeMeasure(bool stop);     // Time in milliseconds
+
+  void      setCondCounter(unsigned int cntVal);  // Set condition counter
+  bool      condOpen();                           // Ask for open conditions
+
+  // -------------------------------------------------------------------------
+  // debug functions
+  // -------------------------------------------------------------------------
+  //
+  int       getDelay();
+
+};
+
+
+
+
+// ---------------------------------------------------------------------------
+#endif
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/StateMachine/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/StateMachine/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..4dbee447df0f4ef48684a8d78288826ff419108f
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/StateMachine/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "StateMachine",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfBuf.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfBuf.h
new file mode 100644
index 0000000000000000000000000000000000000000..c2f41d77dacb2618fee37685820740222e70b8b3
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfBuf.h
@@ -0,0 +1,48 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   IntrfBuf.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   22. November 2021
+//
+// Eine Schnittstelle zu Puffern (speziell Ringpuffer für Kommunikation)
+//
+
+#ifndef IntrfBuf_h
+#define IntrfBuf_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+
+
+class IntrfBuf
+{
+public:
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+
+
+  // --------------------------------------------------------------------------
+  // Datenzugriffe
+  // --------------------------------------------------------------------------
+  //
+  virtual bool  getByteSnd(byte *dest);   // Byte aus Puffer zum Senden holen
+                                          // Rückgabe FALSE: Puffer leer
+
+  virtual void  putByteRec(byte b);       // Byte vom Empfang an Puffer geben
+
+  virtual int   putSeq(byte *msg, int n); // Bytefolge an Puffer übergeben
+
+};
+
+// ----------------------------------------------------------------------------
+#endif  // IntrfBuf_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfGpio.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfGpio.h
new file mode 100644
index 0000000000000000000000000000000000000000..d37fdf6765d077ac1bc20944080dc257f55e0a8a
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfGpio.h
@@ -0,0 +1,122 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   IntrfGpio.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   04. April 2022
+//
+// Eine Schnittstelle zu den unterschiedlichen Ports in Mikrocontrollern
+//
+
+#ifndef IntrfGpio_h
+#define IntrfGpio_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+
+typedef enum _ifPortNumber
+{
+  ifPort0,
+  ifPort1,
+  ifPort2,
+  ifPort3,
+  ifPort4,
+  ifPort5,
+  ifPort6,
+  ifPort7,
+  ifPort8,
+  ifPort9
+} ifPortNumber;
+
+typedef enum
+{
+  GEnoError,
+  GEcdictPar
+} GpioError;
+
+typedef struct _GpioMask
+{
+  dword       port;
+  dword       pins;
+} GpioMask, *GpioMaskPtr;
+
+typedef struct _GpioExtMask
+{
+  dword         port;
+  dword         pins;
+  _GpioExtMask  *next;
+} GpioExtMask, *GpioExtMaskPtr;
+
+typedef struct _GpioRef
+{
+  dword     *ioPtr;
+  dword     pins;
+} GpioRef, *GpioRefPtr;
+
+typedef struct _GpioExtRef
+{
+  dword       *ioPtr;
+  dword       pins;
+  _GpioExtRef *next;
+} GpioExtRef, *GpioExtRefPtr;
+
+typedef struct _GpioExtVal
+{
+  dword       value;
+  _GpioExtVal *next;
+} GpioExtVal, *GpioExtValPtr;
+
+// Spezifikation der Schnittstellentreiber
+//
+#define IfDrvInput          0x0000
+#define IfDrvOutput         0x0001
+#define IfDrvStrongHigh     0x0002
+#define IfDrvStrongLow      0x0004
+#define IfDrvOpenDrain      0x0008
+#define IfDrvOpenSource     0x0010
+#define IfDrvPullUp         0x0020
+#define IfDrvPullDown       0x0040
+#define IfDrvPullStrong     0x0080
+
+typedef enum
+{
+  ArdD2D5,
+  ArdA0A3,
+  ArdA4A5,
+  ArdA0A5,
+  ArdA4A7,
+  ArdA0A7
+} ArdMask;
+
+class IntrfGpio
+{
+public:
+
+  //virtual ~IntrfGpio();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  virtual GpioError config(int nr, unsigned int cnfBits, GpioExtRefPtr refPtr);
+  virtual GpioError config(int nrFrom, int nrTo, unsigned int cnfBits, GpioExtRefPtr refPtr);
+  virtual GpioError config(GpioExtMask mask, unsigned int cnfBits, GpioExtRefPtr refPtr);
+  virtual GpioError configArd(ArdMask ardMask, unsigned int cnfBits);
+
+  // --------------------------------------------------------------------------
+  // Anwendungsfunktionen
+  // --------------------------------------------------------------------------
+  //
+  virtual void      read(GpioExtRefPtr refPtr, GpioExtValPtr valPtr);
+  virtual dword     readArd(ArdMask ardMask);
+
+  virtual void      write(GpioExtRefPtr refPtr, GpioExtValPtr valPtr);
+  virtual void      writeArd(ArdMask ardMask, dword value);
+  virtual void      set(GpioExtRefPtr refPtr);
+  virtual void      clr(GpioExtRefPtr refPtr);
+};
+
+// ----------------------------------------------------------------------------
+#endif //IntrfGpio_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfRadio.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfRadio.h
new file mode 100644
index 0000000000000000000000000000000000000000..470277ab4f365581507365d1f412a47683463662
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfRadio.h
@@ -0,0 +1,154 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   IntrfRadio.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   9. Mai 2021
+//
+// Eine Schnittstelle zu den unterschiedlichen Transceivern in Mikrocontrollern
+// oder Boards mit Mikrocontrollern und Radio-Transceivern
+//
+
+#ifndef IntrfRadio_h
+#define IntrfRadio_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+#include "bleSpec.h"
+
+typedef struct _TxState
+{
+  unsigned int  prgLoopPrep;
+  unsigned int  evtLoopRampUp;
+  unsigned int  evtLoopTrans;
+  byte  *       txBufferPtr;
+} TxState, *TxStatePtr;
+
+typedef struct  _Channel
+{
+  int   idx;
+  int   freq;
+} Channel, *ChannelPtr;
+
+// Zustand des Datenempfangs (Bit)
+//
+#define RECSTAT_ADDRESS   0x0001
+#define RECSTAT_PAYLOAD   0x0002
+#define RECSTAT_END       0x0004
+#define RECSTAT_DISABLED  0x0008
+#define RECSTAT_CRCOK     0x0010
+
+// Modi für das Senden von Telegrammen
+//
+typedef enum _TxMode
+{
+  txmBase,      // Einzelne Sendung, Endezustand DISABLED
+  txmRepStart,  // Wiederholte Sendung Start, Endezustand END
+  txmRepCont,   // Wiederholte Sendung Fortsetzung, Endezustand END
+  txmRepEnd,    // Wiederholte Sendung Ende, Endezustand DISABLED
+  txmReadPrep,  // Einzelne Sendung mit Empfangsvorbereitung, Endezustand READY
+  txmRead,      // Einzelne Sendung und Empfang, Endezustand END
+  txmPoll,      // Einzelne Sendung und Empfang, Endezustand DISABLED
+  txmReadS,     // Einzelne sendung und Empfang mit Daten, Endezustand END
+  txmRespE,     // Empfang für spezifische Antwort (leeres Polling)
+  txmResp       // Empfang für spezifische Antwort (Datenübertragung)
+} TxMode;
+//
+#define NrOfTxModes   10
+
+// Protokollspezifische Adresseninhalte und Festlegungen
+//
+#define PollPduSize   8
+#define PollAdrSize   6
+
+// len = PduMem[1]
+#define BLE_LEN       pduMem[1]
+// Adr[1] = PduMem[3]
+#define BLE_ADR1      pduMem[3]
+
+#define SOAAP_NAK     0x40
+#define SOAAP_EADR    0x80
+
+// Modeabhängige Statistikdaten
+//
+typedef struct _TxStatistics
+{
+  TxMode      mode;
+  dword       interrupts;
+  dword       recs;
+  dword       sendings;
+  dword       aliens;
+  dword       wrongs;
+  dword       pollAcks;
+  dword       pollNaks;
+  dword       crcErrors;
+  dword       intErrors;
+  byte        memDumpRec[16];
+  byte        memDumpSnd[16];
+} TxStatistics, *TxStatisticsPtr;
+
+class IntrfRadio
+{
+public:
+  // Kanalfrequenzen (Offsets zur Basisfrequenz) und Whitening-Preset
+  // Die ersten 3 Kanäle sind Bewerbungskanäle.
+  // Daran anschließend sind die Kanäle grob nach Frequenz einsortiert.
+  // Diese Liste kann zur Laufzeit an Störungen angepasst werden und wird aufsteigend angewendet
+  //
+  Channel channelList[40] =
+  {
+      {37, 2} , {38,26} , {39,80} , { 1, 6} , { 3,10} , { 5,14} , { 7,18} , { 9,22} ,
+      {12,30} , {14,34} , {16,38} , {18,42} , {20,46} , {22,50} , {24,54} , {26,58} ,
+      {28,62} , {30,66} , {32,70} , {34,74} , {35,76} , { 2, 8} , { 4,12} , { 6,16} ,
+      { 8,20} , {33,72} , {31,68} , {13,32} , {15,36} , {17,40} , {19,44} , {21,48} ,
+      {23,52} , {25,56} , {27,60} , {29,64} , {11,28} , {10,24} , { 0, 4} , {36,78}
+  };
+
+  //virtual ~IntrfRadio();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  virtual void  begin();
+  virtual void  setAccessAddress(dword addr);
+  virtual void  setPacketParms(blePduType type);
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+  virtual void  setChannel(int chnr);         // Schalten physikalischer Kanal
+  virtual int   sendSync(bcPduPtr inPduPtr, TxStatePtr refState);
+
+  virtual void  send(bcPduPtr inPduPtr, TxMode txMode);
+  virtual void  send(bcPduPtr inPduPtrE, bcPduPtr inPduPtrS, TxMode txMode, bool newValues);
+  // Senden (und/oder Empfang) eines Telegramms in einem festgelegten Modus
+
+  virtual void  disable(TxMode txMode);       // Funk AUS für Betriebswechsel
+  virtual bool  disabled(TxMode txMode);      // Abfrage, ob ausgeschaltet
+  virtual void  cont(TxMode txMode);          // aktuellen Vorgang fortsetzen
+  virtual bool  fin(TxMode txMode, bool *err);  // Abfrage ob aktueller Vorgang beendet
+// virtual int   getRecData(bcPduPtr data, TxMode txMode, int max); // Lennard: Deaktiviert
+
+  virtual int   startRec();                   // Datenempfang starten
+  virtual int   contRec();                    // Datenempfang fortsetzen
+  virtual int   endRec();                     // Datenempfang beenden
+  virtual int   checkRec();                   // Zustand Datenempfang feststellen
+  virtual int   getRecData(bcPduPtr data, int max);  // Empfangene Daten lesen
+
+  virtual void  setPower(int DBm);            // Leistung des Senders in DBm
+
+  // --------------------------------------------------------------------------
+  // Datenzugriffe
+  // --------------------------------------------------------------------------
+  //
+  virtual int   getStatistics(TxStatisticsPtr dest);
+  virtual int   getState();                   // Chip-abhängiger Funk-Status
+
+};
+
+// ----------------------------------------------------------------------------
+#endif  // IntrfRadio_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfSerial.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfSerial.h
new file mode 100644
index 0000000000000000000000000000000000000000..909a810a3282ef0e1ee36e79286bd5c4df0bb929
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfSerial.h
@@ -0,0 +1,101 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   IntrfSerial.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   17. November 2021
+//
+// Eine Schnittstelle zu den seriellen Schnittstellen in Mikrocontrollern
+// oder Boards mit Mikrocontrollern und seriellen schnittstellen
+//
+
+#ifndef IntrfSerial_h
+#define IntrfSerial_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+#include "IntrfBuf.h"
+
+typedef enum _SerSpeed
+{
+  Baud1200,
+  Baud2400,
+  Baud4800,
+  Baud9600,
+  Baud14400,
+  Baud19200,
+  Baud28800,
+  Baud31250,
+  Baud38400,
+  Baud56000,
+  Baud57600,
+  Baud76800,
+  Baud115200,
+  Baud230400,
+  Baud250000,
+  Baud460800,
+  Baud921600,
+  Baud1Meg
+} SerSpeed;
+
+typedef enum _SerType
+{
+  stStd,      // Typischer Ausgang ca. 2 mA
+  stPow,      // Starker Ausgang ca. 10 mA
+  stCur       // Open Collector/Drain ca. 10 mA Stromschleife
+} SerType;
+
+typedef struct _SerParams
+{
+  int         inst;           // Nummer (Index) der Ser-Instanz
+  int         txdPort;        // Nummer (Index) des Port fuer TX
+  int         txdPin;         // Nummer (Index) des Pin fuer TX
+  int         rxdPort;        // Nummer (Index) des Port fuer RX
+  int         rxdPin;         // Nummer (Index) des Pin fuer RX
+  SerSpeed    speed;          // Bitrate (Baud)
+  SerType     type;           // Ausgang
+} SerParams, *SerParamsPtr;
+
+typedef enum _SerError
+{
+  SEnoError
+} SerError;
+
+
+class IntrfSerial
+{
+public:
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  virtual void  begin(SerParamsPtr serParPtr, IntrfBuf * bufferIf);
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+  virtual void resuSend();    // Fortsetzen des Interrupt-Sendebetriebs
+  virtual void startSend();   // Starten des Sendebetriebs
+  virtual void stopSend();    // Anhalten des Sendebetriebs
+
+  virtual void startRec();    // Starten des Empfangsbetriebs
+  virtual void stopRec();     // Anhalten des Empfangsbetriebs
+
+
+  // --------------------------------------------------------------------------
+  // Datenzugriffe
+  // --------------------------------------------------------------------------
+  //
+  virtual bool condSend(byte c);  // Bedingtes Senden eines Zeichens
+
+  virtual int   getLastError();   // Letzten Fehler lesen (Bits)
+  virtual int   getAnyError();    // Alle vorgekommenen Fehlerbits
+  virtual dword getErrCount();    // Anzahl der Fehler lesen
+
+};
+
+// ----------------------------------------------------------------------------
+#endif  // IntrfRadio_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfTimer.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfTimer.h
new file mode 100644
index 0000000000000000000000000000000000000000..2777f5ba7b80b1116baa9030c452037dc4507952
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfTimer.h
@@ -0,0 +1,55 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   IntrfTimer.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   29. Juni 2021
+//
+// Eine Schnittstelle zu den unterschiedlichen Timern in Mikrocontrollern
+//
+
+#ifndef IntrfTimer_h
+#define IntrfTimer_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+
+typedef enum _ifTimerNumber
+{
+  ifTimer0,
+  ifTimer1,
+  ifTimer2,
+  ifTimer3,
+  ifTimer4,
+  ifTimer5,
+  ifTimer6,
+  ifTimer7,
+  ifTimer8,
+  ifTimer9
+} ifTimerNumber;
+
+class IntrfTimer
+{
+public:
+
+  //virtual ~IntrfTimer();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  virtual void  setMilli(ifTimerNumber timNr, int milliSec, int repeat);
+  //virtual void  setMilli(ifTimerNumber timNr, int milliSec, int repeat, dword ISR);
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+  virtual bool  milli();        // Abfrage des Timer, <true> wenn abgelaufen
+
+};
+
+// ----------------------------------------------------------------------------
+#endif //IntrfTimer_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfTw.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfTw.h
new file mode 100644
index 0000000000000000000000000000000000000000..f0d97b622e98f8f48ee8f066912caf1b4723bc89
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/IntrfTw.h
@@ -0,0 +1,125 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   IntrfTw.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   3. Juni 2021
+//
+// Eine Schnittstelle zu den unterschiedlichen I2C Schnittstellen
+// oder Boards mit geeigneten Mikrocontrollern
+//
+
+#ifndef IntrfTw_h
+#define IntrfTw_h
+// ----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+
+typedef enum _TwiDevType
+{
+  TwiMaster,
+  TwiSlave
+} TwiDevType;
+
+typedef enum _TwiSpeed
+{
+  Twi100k,      // Standard, sollte unterstützt werden
+  Twi250k,
+  Twi400k       // Schnell, sollte unterstützt werden
+} TwiSpeed;
+
+typedef struct _TwiParams
+{
+  int         inst;           // Nummer (Index) der Twi-Instanz
+  TwiDevType  type;           // Auswahl Master/Slave
+  int         clkPort;        // Nummer (Index) des Port fuer Takt
+  int         clkPin;         // Nummer (Index) des Pin fuer Takt
+  int         dataPort;       // Nummer (Index) des Port fuer Daten
+  int         dataPin;        // Nummer (Index) des Pin fuer Daten
+  TwiSpeed    speed;
+} TwiParams, *TwiParamsPtr;
+
+typedef enum _TwiError
+{
+  TEnoError
+} TwiError;
+
+typedef enum _TwiStatus
+{
+  TwStUnborn,
+  TwStRdReq,
+  TwStWrReq,
+  TwStSent,
+  TwStRecvd,
+  TwStFin     = 0x0080,
+  TwStError   = 0x0100,
+  TwStOverrun = 0x0101,
+  TwStAdrNak  = 0x0102,
+  TwStDataNak = 0x0104
+} TwiStatus;
+
+typedef struct _TwiByte
+{
+  TwiStatus   twiStatus;
+  byte        value;
+} TwiByte, *TwiBytePtr;
+
+typedef struct _TwiWord
+{
+  TwiStatus   twiStatus;
+  word        value;
+} TwiWord, *TwiWordPtr;
+
+typedef struct _TwiByteSeq
+{
+  TwiStatus   twiStatus;
+  int         len;
+  byte        *valueRef;
+} TwiByteSeq, *TwiByteSeqPtr;
+
+
+class IntrfTw
+{
+public:
+  // --------------------------------------------------------------------------
+  // Konstruktoren und Initialisierungen
+  // --------------------------------------------------------------------------
+  //
+  virtual TwiError begin(TwiParamsPtr inParPtr);
+
+  //virtual ~IntrfTw();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  virtual void getParams(TwiParamsPtr parPtr);
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+
+  // --------------------------------------------------------------------------
+  // Datenaustausch
+  // --------------------------------------------------------------------------
+  //
+  // asynchrone Kommunikation, Zustand in TwiByte.twiStatus
+  //
+  virtual TwiError sendByte(int adr, TwiBytePtr refByte);
+  virtual TwiError sendByteReg(int adr, int reg, TwiBytePtr refByte);
+  virtual TwiError recByteReg(int adr, int reg, TwiBytePtr refByte);
+  virtual TwiError recByteRegSeq(int adr, int reg, TwiByteSeqPtr refByteSeq);
+
+  // synchrone Kommunikation
+  //
+  virtual TwiStatus writeByteReg(int adr, int reg, byte value);
+  virtual int       readByteReg(int adr, int reg);
+  virtual TwiStatus readByteRegSeq(int adr, int reg, TwiByteSeqPtr refByteSeq);
+
+};
+
+// ----------------------------------------------------------------------------
+#endif  // IntrfTw_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/SoaapBleMaster.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/SoaapBleMaster.h
new file mode 100644
index 0000000000000000000000000000000000000000..98bd22eb99c0839cf9f5ad554dd116b21c0afd81
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/SoaapBleMaster.h
@@ -0,0 +1,63 @@
+// ----------------------------------------------------------------------------
+//                              SoaapBleMaster.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:   1. November 2021
+// Letzte Bearbeitung: 15. März 2022
+//
+
+#ifndef SoaapBleMaster_h
+#define SoaapBleMaster_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
+
+//#define TEST001
+// Ausgaben an serielle schnittstelle zur Prüfung der ap-Zustandsmaschine
+
+#include  "LoopCheck.h"
+#include  "StateMachine.h"
+#include  "nRF52840Radio.h"
+#include  "BlePoll.h"
+#include  "ComRingBuf.h"
+#include  "nRF52840Ser.h"
+#include  "SoaapMsg.h"
+#include  "Monitor.h"
+
+// ----------------------------------------------------------------------------
+// Vorwärtsreferenzen
+// ----------------------------------------------------------------------------
+//
+void ctrlIdle();
+void ctrlInit();
+void sendCtrl();
+void checkCtrl();
+
+void apInit();
+void apWaitDE();
+void apWaitMeas();
+void apProcMeas();
+
+
+#ifdef DebugTerminal
+// ----------------------
+void smInit() ;
+void smCheckJobs() ;
+void smDebDword() ;
+void smCtrlPolling() ;
+void smWaitPolling() ;
+void smReadPollValues() ;
+void smCheckSer();
+// ----------------------
+#endif
+
+#endif /* SoaapBleMaster_h */
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/SoaapBleSlave.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/SoaapBleSlave.h
new file mode 100644
index 0000000000000000000000000000000000000000..f3ae7f7500126e35f19524b4188a3044d43dfaf4
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/SoaapBleSlave.h
@@ -0,0 +1,43 @@
+#ifndef SoaapBleSlave_h
+#define SoaapBleSlave_h
+
+#include  "LoopCheck.h"
+#include  "StateMachine.h"
+#include  "nRF52840Radio.h"
+#include  "BlePoll.h"
+#include  "ComRingBuf.h"
+#include  "nRF52840Ser.h"
+#include  "SoaapMsg.h"
+#include  "Monitor.h"
+#include "nRF52840Twi.h"
+#include "SensorLSM9DS1.h"
+#include "nRF52840Gpio.h"
+
+//#define SlaveACM1
+bool sendData(PlpType plpType, byte *dest);
+bool getValues(PlpType plpType, byte *dest);
+void smInit();
+void smCheckJobs();
+void smCheckSens();
+
+void smDebDword();
+void smCtrlPolling();
+void smWaitPolling();
+void smSensReset1();
+void smSensReset2();
+
+void smSensGetValues1();
+void smSensGetValues2();
+void smSensGetValues3();
+void smSensGetValues4();
+void smSensGetValues5();
+void smSensGetValues6();
+void smSensGetValues7();
+
+void smSensGetSoaapValues();
+void smSensDebugValues();
+
+void smSensGetErrors();
+void smSensGetTimeOuts();
+void smSensGetRunCounts();
+#endif
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/arduinoDefs.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/arduinoDefs.h
new file mode 100644
index 0000000000000000000000000000000000000000..4830c5d860683336800903c177b10c53fcb73736
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/arduinoDefs.h
@@ -0,0 +1,53 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Definitions instead Arduino
+// Datei:   arduinoDefs.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+// These definitions shall encapsulate the Arduino environment
+// The file should be included instead of Arduino.h for arbitrary environments
+//
+
+#ifndef _arduinoDefs_h
+#define _arduinoDefs_h
+
+// ---------------------------------------------------------------------------
+// Simple type definitions and settings
+// ---------------------------------------------------------------------------
+//
+#undef  byte
+#undef  word
+#undef  dword
+
+#define byte unsigned char
+#define word unsigned short
+#define dword unsigned long
+
+#ifdef smnLinGcc64
+
+#undef  byte
+#undef  word
+#undef  dword
+
+#define byte unsigned char
+#define word unsigned short
+#define dword unsigned int
+
+#endif
+
+// ---------------------------------------------------------------------------
+// Complex type definitions
+// ---------------------------------------------------------------------------
+// The following types are more complex definitions with Arduino.
+// But here simple types are used if possible, because we only care for the
+// environment that is needed by Social Manufacturing Network
+//
+
+#ifndef IPAddress
+  #define IPAddress   byte *
+#endif
+
+
+#endif  // _arduinoDefs_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/bleSpec.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/bleSpec.h
new file mode 100644
index 0000000000000000000000000000000000000000..a4993a39fee05fa5162fc888acf0804e5fc59254
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/bleSpec.h
@@ -0,0 +1,86 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   bleSpec.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   8. Mai 2021
+//
+// Der Inhalt dieser Datei ist der Blootooth Spezifikation 5.2 entnommen
+// bzw. daran angelehnt.
+// Insbesondere dem Teil 6, Low Energy Controller
+//
+
+#ifndef bleSpec_h
+#define bleSpec_h
+// ----------------------------------------------------------------------------
+
+#ifndef octet
+typedef unsigned char octet;
+#endif
+
+#define VersionBTS  52
+#define TypeBTS     BLE
+
+// ----------------------------------------------------------------------------
+// Festlegungen für die Bewerbungskanäle (Advertizing Physical Channels)
+// ----------------------------------------------------------------------------
+
+// Basisfrequenz in MHz
+#define BaseFrequency   2400
+
+// Zugriffsadresse (Access Address)
+#define AdvAccAddr  0x8E89BED6
+
+// CRC-Polynom
+#define PolynomCRC  0x0100065B
+
+// CRC-Startwert
+#define AdvStartCRC 0x555555
+
+// Geräteadresse
+typedef octet  BD_ADR[6];
+
+// Telegrammtypen (PDU Types)
+#define ADV_IND           0x0
+#define ADV_DIRECT_IND    0x1
+#define ADV_NONCONN_IND   0x2
+#define SCAN_REQ          0x3
+#define SCAN_RSP          0x4
+#define CONNECT_IND       0x5
+#define ADV_SCAN_IND      0x6
+
+// Kennzeichnung Art der Geräteadresse (Device Address Mark, TxAdd = 1, random)
+#define DevAdrType  0x2
+
+// Telegrammkopf ohne Längenbyte
+#define HeadS0B     ((ADV_NONCONN_IND << 4) | DevAdrType)
+#define HeadS0BS    ((ADV_SCAN_IND << 4) | DevAdrType)
+
+// Datenstruktur für das zu sendende Telegramm
+// bei der Bewerbung (Advertising)
+//
+typedef struct _bcPdu         // Beacon - PDU
+{
+  byte  head;       // Header = PDU-Typ und Adresskennung (S0 bei nRF52840)
+  byte  len;        // Länge des Telegramms inkl. Adresse (LENGTH bei nRF52840)
+  byte  adr0;       // niedrigstwertiges Adressbyte (S1 bei nRF52840)
+  byte  adr1;       //
+  byte  adr2;       //      Das ist die Geräteadresse, die hier wahlfrei ist
+  byte  adr3;       //      Sie wird zur Identifikation des Gerätes verwendet
+  byte  adr4;       //
+  byte  adr5;       // höchstwertiges Addressbyte
+  byte  data[31];   // Nutzdaten (maximale Anzahl nach BLE-Spez.)
+} bcPdu, *bcPduPtr;
+
+// Telegrammtypen
+//
+typedef enum  _blePduType
+{
+  bptAdv,           // Standard-Bewerbungstelegramm
+  bptAux
+} blePduType;
+
+// ----------------------------------------------------------------------------
+#endif  // bleSpec_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/environment.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/environment.h
new file mode 100644
index 0000000000000000000000000000000000000000..311c3331bd39a97884ba12b85375b08a418813d0
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/environment.h
@@ -0,0 +1,126 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   environment.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#ifndef _environment_h
+#define _environment_h
+
+#ifdef  smnDEFBYBUILD
+
+// Alternative Festlegungen durch Settings in der Build-Umgebung der IDE
+// Das erspart die Verwaltung mehrerer/unterschiedlicher environment.h Dateien
+// in den Projekten
+//
+  #ifdef smnUBUNTU_ECLIPSE
+  // Meine Entwicklungs- und Testumgebung auf dem Notebook unter UBUNTU
+  // für Sketche, die auf dem PC laufen (u.a. zum Debuggen)
+
+    #define smnSimLinux
+    #define smnNotebook
+    #define smnLINUX
+    #define smnDebugLinuxConsole
+
+  #endif
+
+  #ifdef smnWIN32_VS
+  // Meine Entwicklungs- und Testumgebung auf dem Notebook unter Windows
+  // für Sketche, die auf dem PC laufen (u.a. zum Debuggen)
+
+    #define smnSimWindows
+    #define smnNotebook
+    #define smnWIN32
+    #define smnDebugWindowsConsole
+
+  #endif
+
+  #ifdef smnSLOELIN
+  // Mit Sloeber auf Ubuntu/Linux für Entwicklungen zum ESP32
+  //
+
+    #define smnDebugArduino
+    #define smnSloeber
+    #define smnESP32_DEV
+    #define smnESP32
+    #define smnSerial Serial
+  #endif
+
+  #ifdef smnSLOEDUE
+  // Mit Sloeber auf Ubuntu/Linux für Entwicklungen zum Arduino Due
+  //
+
+    #define smnDebugArduino
+    #define smnSloeber
+    #define smnArduinoDUE
+    #define smnArduinoShieldEth
+    #define smnSAM3X
+    #define smnSerial Serial
+  #endif
+
+  #ifdef smnNANOBLE33
+    #define smnDebugArduino
+    #define smnSloeber
+    #define smnArduinoNanoBLE33
+    #define smnNRF52840
+    #define smnSerial Serial
+  #endif
+
+  #ifdef smnSLOESAMD21
+    #define smnDebugArduino
+    #define smnSloeber
+    #define smnArduinoZeroSamD21
+    #define smnSAMD21G18
+    #define smnSD21MINI
+    #define smnSerial SerialUSB
+  #endif
+
+#else
+
+// Die Definitionen werden hier in den meisten Fällen grundsetzlich ausgewertet,
+// nicht über spezifische Werte.
+// Die Abfrage geschieht also mit #ifdef
+// Daraus folgt hier eine Liste der Möglichkeiten, die beliebig erweitert werden kann.
+// Die Auswahl erfolgt schließlich über aus- bzw. einkommentieren.
+//
+
+// Übergeordnete Festlegungen
+// ---------------------------------------------------------------------------
+//
+#define smnDebugArduino
+//#define smnDebugLinuxConsole
+//
+
+// IDE, Editor, etc.
+// ---------------------------------------------------------------------------
+//
+//#define smnSimLinux
+#define smnSloeber
+//#define smnArduinoIDE
+//#define smnPlatformIO
+
+// Hardware, Boards, etc.
+// ---------------------------------------------------------------------------
+//
+//#define smnNotebook
+//#define smnESP32_DEV
+#define smnNodeMCU10
+//#define smnArduinoDUE
+//#define smnArduinoShieldEth
+//#define smnArduinoShieldWiFi
+#define smnSerial Serial
+
+// Prozessoren, Microcontroller, Betriebssysteme, etc.
+// ---------------------------------------------------------------------------
+//
+//#define smnLINUX
+//#define smnESP32
+#define smnESP8266
+//#define smnSAM3X
+
+#endif  // smnDEFBYBUILD
+
+#endif  // _environment_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..9e3641f6fb0e38c5e74b3c3cee96651efbb2fdaa
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "environment",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/socManNetUser.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/socManNetUser.h
new file mode 100644
index 0000000000000000000000000000000000000000..4809a3b98de3da0b84f1f25ba7cda8af91bb2672
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/environment/socManNetUser.h
@@ -0,0 +1,315 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Broadcast Socket Interface
+// Datei:   socManNetUser.h
+// Editor:  Igor Farber, Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#ifndef _socManNetUser_h
+#define _socManNetUser_h
+
+#define smnMULTIBOARDS
+
+#define smnBOARD001
+//#define smnBOARD002
+//#define smnBOARD003
+//#define smnBOARD004
+//#define smnBOARD005
+//#define smnBOARD006
+
+
+
+#ifndef smnMyNet
+// ***************************************************************************
+// define smnMyNet in project properties (Arduino) with -DsmnMyNet
+// to use your own network definition file included at the end of this file
+
+
+//---------------------------------------------------------------------------//
+//-------------------------------------------------------------------------
+// MAC-Adresse des Ethernet-Shield
+//-------------------------------------------------------------------------
+#ifdef  smnMULTIBOARDS
+
+  #ifdef  smnBOARD001
+    #define LOCAL_MAC_ADR_B0 0x90
+    #define LOCAL_MAC_ADR_B1 0xa2
+    #define LOCAL_MAC_ADR_B2 0xda
+    #define LOCAL_MAC_ADR_B3 0x0f
+    #define LOCAL_MAC_ADR_B4 0x1b
+    #define LOCAL_MAC_ADR_B5 0x84
+    // MAC-Adresse
+
+    #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-1b-84"
+    // MAC-Adresse als String
+  #endif
+
+  #ifdef  smnBOARD002
+    #define LOCAL_MAC_ADR_B0 0x90
+    #define LOCAL_MAC_ADR_B1 0xa2
+    #define LOCAL_MAC_ADR_B2 0xda
+    #define LOCAL_MAC_ADR_B3 0x0f
+    #define LOCAL_MAC_ADR_B4 0x63
+    #define LOCAL_MAC_ADR_B5 0xb0
+    // MAC-Adresse
+
+    #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-63-b0"
+    // MAC-Adresse als String
+  #endif
+
+  #ifdef  smnBOARD003
+    #define LOCAL_MAC_ADR_B0 0x90
+    #define LOCAL_MAC_ADR_B1 0xa2
+    #define LOCAL_MAC_ADR_B2 0xda
+    #define LOCAL_MAC_ADR_B3 0x0f
+    #define LOCAL_MAC_ADR_B4 0x5b
+    #define LOCAL_MAC_ADR_B5 0xe1
+    // MAC-Adresse
+
+    #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-5b-e1"
+    // MAC-Adresse als String
+  #endif
+
+  #ifdef  smnBOARD004
+    #define LOCAL_MAC_ADR_B0 0x90
+    #define LOCAL_MAC_ADR_B1 0xa2
+    #define LOCAL_MAC_ADR_B2 0xda
+    #define LOCAL_MAC_ADR_B3 0x0f
+    #define LOCAL_MAC_ADR_B4 0x5b
+    #define LOCAL_MAC_ADR_B5 0x8f
+    // MAC-Adresse
+
+    #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-62-d0"
+    // MAC-Adresse als String
+  #endif
+
+  #ifdef  smnBOARD005
+    #define LOCAL_MAC_ADR_B0 0x90
+    #define LOCAL_MAC_ADR_B1 0xa2
+    #define LOCAL_MAC_ADR_B2 0xda
+    #define LOCAL_MAC_ADR_B3 0x0f
+    #define LOCAL_MAC_ADR_B4 0x62
+    #define LOCAL_MAC_ADR_B5 0xd0
+    // MAC-Adresse
+
+    #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-62-d0"
+    // MAC-Adresse als String
+  #endif
+
+  #ifdef  smnBOARD006
+    #define LOCAL_MAC_ADR_B0 0x90
+    #define LOCAL_MAC_ADR_B1 0xa2
+    #define LOCAL_MAC_ADR_B2 0xda
+    #define LOCAL_MAC_ADR_B3 0x0f
+    #define LOCAL_MAC_ADR_B4 0x62
+    #define LOCAL_MAC_ADR_B5 0xd0
+    // MAC-Adresse
+
+    #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-62-d0"
+    // MAC-Adresse als String
+  #endif
+
+#else
+  #define LOCAL_MAC_ADR_B0 0x90
+  #define LOCAL_MAC_ADR_B1 0xa2
+  #define LOCAL_MAC_ADR_B2 0xda
+  #define LOCAL_MAC_ADR_B3 0x0f
+  #define LOCAL_MAC_ADR_B4 0x62
+  #define LOCAL_MAC_ADR_B5 0xd0
+  // MAC-Adresse
+
+  #define LOCAL_MAC_ADR_STR (char *) "90-a2-da-0f-62-d0"
+  // MAC-Adresse als String
+#endif
+
+#define LOCAL_PORT      4100
+// Portnummer lokal
+
+#define BROADCAST_PORT  4100
+// Portnummer Rundruf
+
+#define CONFIG_PORT     4001
+
+//-------------------------------------------------------------------------
+// Sub-Netz-Maske
+//-------------------------------------------------------------------------
+#define SUB_NET_MASK_B0 255
+#define SUB_NET_MASK_B1 255
+#define SUB_NET_MASK_B2 255
+#define SUB_NET_MASK_B3 0
+
+//----------------------------------------------------------------------------
+// IP-Adresse, ist auf das verwendete Netzwerk anzupassen (kein DHCP)
+//----------------------------------------------------------------------------
+
+// Das Netzwerk an sich
+#define LOCAL_IP_B0 192
+#define LOCAL_IP_B1 168
+#define LOCAL_IP_B2 99
+
+#define BROADCAST_IP_B0 192
+#define BROADCAST_IP_B1 168
+#define BROADCAST_IP_B2 99
+#define BROADCAST_IP_B3 255
+
+#ifdef smnMULTIBOARDS
+
+  #ifdef smnBOARD001
+
+    // IP-Lokal
+    // ----------------------------------------------
+    //
+    #define LOCAL_IP_B3 201
+    #define LOCAL_IP_ADR_STR (char *) "192.168.99.201"
+
+    // IP-Broadcast
+    // ----------------------------------------------
+    //
+    #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+  #endif
+
+
+  #ifdef smnBOARD002
+
+    // IP-Lokal
+    // ----------------------------------------------
+    //
+    #define LOCAL_IP_B3 202
+    #define LOCAL_IP_ADR_STR (char *) "192.168.99.202"
+
+    // IP-Broadcast
+    // ----------------------------------------------
+    //
+    #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+  #endif
+
+
+  #ifdef smnBOARD003
+
+    // IP-Lokal
+    // ----------------------------------------------
+    //
+    #define LOCAL_IP_B3 203
+    #define LOCAL_IP_ADR_STR (char *) "192.168.99.203"
+
+    // IP-Broadcast
+    // ----------------------------------------------
+    //
+    #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+  #endif
+
+
+  #ifdef smnBOARD004
+
+    // IP-Lokal
+    // ----------------------------------------------
+    //
+    #define LOCAL_IP_B3 204
+    #define LOCAL_IP_ADR_STR (char *) "192.168.99.204"
+
+    // IP-Broadcast
+    // ----------------------------------------------
+    //
+    #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+  #endif
+
+
+  #ifdef smnBOARD005
+
+    // IP-Lokal
+    // ----------------------------------------------
+    //
+    #define LOCAL_IP_B3 205
+    #define LOCAL_IP_ADR_STR (char *) "192.168.99.205"
+
+    // IP-Broadcast
+    // ----------------------------------------------
+    //
+    #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+  #endif
+
+
+  #ifdef smnBOARD006
+
+    // IP-Lokal
+    // ----------------------------------------------
+    //
+    #define LOCAL_IP_B3 206
+    #define LOCAL_IP_ADR_STR (char *) "192.168.99.205"
+
+    // IP-Broadcast
+    // ----------------------------------------------
+    //
+    #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+  #endif
+
+
+#else
+
+  // IP-Lokal
+  // ----------------------------------------------
+  //
+  #define LOCAL_IP_B3 205
+  #define LOCAL_IP_ADR_STR (char *) "192.168.99.205"
+
+  // IP-Broadcast
+  // ----------------------------------------------
+  //
+  #define BROADCAST_IP_B3 255
+  #define BROADCAST_IP_ADR_STR (char *) "192.168.99.255"
+
+#endif
+
+// IP-Gateway
+// ----------------------------------------------
+//
+#define GATEWAY_IP_B0 192
+#define GATEWAY_IP_B1 168
+#define GATEWAY_IP_B2 99
+#define GATEWAY_IP_B3 1
+
+
+/******************************************************************************
+ * Netzwerkname
+ * Passwort
+ */
+#define SMNSSID "MPZ-Labor"    //Netzwerk Name
+#define SMNPASS "MPZMPZMPZ"    //Netzwerk Passwort
+
+// IP-primaryDNS
+// ----------------------------------------------
+//
+#define PRIMDNS_IP_B0   8
+#define PRIMDNS_IP_B1   8
+#define PRIMDNS_IP_B2   8
+#define PRIMDNS_IP_B3   8
+
+// IP-secondaryDNS
+// ----------------------------------------------
+//
+#define SECDNS_IP_B0    8
+#define SECDNS_IP_B1    8
+#define SECDNS_IP_B2    4
+#define SECDNS_IP_B3    4
+
+#else
+// ***************************************************************************
+  #undef _socManNetUser_h
+  #include MyNetFile
+  // Define MyNetFile in Project-Properties with -DMyNetFile=\"xxxxx\"
+  // and xxxxx being your filename.
+  // Define also smnMyNet with -DsmnMyNet to enter this include directive
+
+#endif // smnMyNet
+// ***************************************************************************
+
+//---------------------------------------------------------------------------//
+#endif // _socManNetUser_h
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Gpio/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Gpio/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..a099c0c6f9c5ede5584dbf9ab24da0199b348548
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Gpio/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "nRF52840Gpio",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Gpio/nRF52840Gpio.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Gpio/nRF52840Gpio.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..47a25ddebcff11a2d9ef003cf83ddf1be250d057
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Gpio/nRF52840Gpio.cpp
@@ -0,0 +1,353 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Gpio.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   29. Juni 2021
+//
+
+#include "nRF52840Gpio.h"
+
+  // --------------------------------------------------------------------------
+  // Konstruktoren
+  // --------------------------------------------------------------------------
+  //
+  nRF52840Gpio::nRF52840Gpio()
+  {
+    gpioPtr = NULL;
+  }
+
+
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+
+dword     nRF52840Gpio::getCnfValue(unsigned int cnfBits)
+{
+  dword tmpMask = 0;
+
+  if(cnfBits & IfDrvPullUp) tmpMask |= GpioPinCnf_PULL(GpioPullUp);
+  if(cnfBits & IfDrvPullDown) tmpMask |= GpioPinCnf_PULL(GpioPullDown);
+
+  if((cnfBits & IfDrvOutput))     // Ausgang **********************************
+  {
+    tmpMask |= GpioPinCnf_DIR;
+
+    if(cnfBits & IfDrvStrongHigh)       // StrongHigh = H1
+    {
+      if(cnfBits & IfDrvOpenSource)     // OpenSource = D0
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveD0H1);
+      else if(cnfBits & IfDrvStrongLow) // StrongLow = H0
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveH0H1);
+      else
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveS0H1);
+    }
+    else if(cnfBits & IfDrvStrongLow)   // StrongLow = H0
+    {
+      if(cnfBits & IfDrvOpenDrain)      // OpenDrain = D1
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveH0D1);
+      else
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveH0S1);
+    }
+    else
+    {
+      if(cnfBits & IfDrvOpenSource)     // OpenSource = D0
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveD0S1);
+      else if(cnfBits & IfDrvOpenDrain) // OpenDrain = D1
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveS0D1);
+      else
+        tmpMask |= GpioPinCnf_DRIVE(GpioDriveS0S1);
+    }
+  }
+  else                            // Eingang **********************************
+  {
+    tmpMask &= 0xFFFFFFFC;
+  }
+
+  return(tmpMask);
+}
+
+GpioError nRF52840Gpio::config(int nrFrom, int nrTo, unsigned int cnfBits, GpioExtRefPtr refPtr)
+{
+  GpioError retv = GEnoError;
+  int       portNum;
+  int       pinNum;
+  dword     tmpMask;
+  dword     cnfValue;
+
+  tmpMask = IfDrvOpenDrain | IfDrvOpenSource;
+  if((cnfBits & tmpMask) == tmpMask) return (GEcdictPar);
+
+  tmpMask = IfDrvPullDown | IfDrvPullUp;
+  if((cnfBits & tmpMask) == tmpMask) return (GEcdictPar);
+
+  cnfValue  = getCnfValue(cnfBits);
+  tmpMask   = 0;
+
+  // Bedienen des angegebenen Bereiches
+  //
+  for(int i = nrFrom; i <= nrTo; i++)
+  {
+    portNum = (i & 0x0E0) >> 5;
+    pinNum  =  i & 0x01F;
+
+    tmpMask |= (1 << i);
+
+    if(portNum == 0)
+      gpioPtr = NrfGpioPtr0;
+    else
+      gpioPtr = NrfGpioPtr1;
+
+      gpioPtr->PIN_CNF[pinNum] = cnfValue;
+  }
+
+  refPtr->ioPtr = (dword *) gpioPtr;
+  refPtr->pins  = tmpMask;
+
+  return(retv);
+}
+
+GpioError nRF52840Gpio::config(int nr, unsigned int cnfBits, GpioExtRefPtr refPtr)
+{
+  return(config(nr,nr,cnfBits, refPtr));
+}
+
+GpioError nRF52840Gpio::config(GpioExtMask mask, unsigned int cnfBits, GpioExtRefPtr refPtr)
+{
+  GpioError retv = GEnoError;
+  dword     cnfVal;
+
+  cnfVal = IfDrvOpenDrain | IfDrvOpenSource;
+  if((cnfBits & cnfVal) == cnfVal) return (GEcdictPar);
+
+  cnfVal = IfDrvPullDown | IfDrvPullUp;
+  if((cnfBits & cnfVal) == cnfVal) return (GEcdictPar);
+
+  cnfVal = getCnfValue(cnfBits);
+
+  // Bedienen des angegebenen Bereiches
+  //
+  dword chkMask = 1;
+
+  for(int i = 0; i < 32; i++)
+  {
+    if(mask.port == 0)
+      gpioPtr = NrfGpioPtr0;
+    else
+      gpioPtr = NrfGpioPtr1;
+
+    if(mask.pins & chkMask)
+      gpioPtr->PIN_CNF[i] = cnfVal;
+
+    chkMask <<= 1;
+  }
+
+  if(refPtr != NULL)
+  {
+    refPtr->ioPtr = (dword *) gpioPtr;
+    refPtr->pins  = mask.pins;
+  }
+
+  return(retv);
+}
+
+GpioError nRF52840Gpio::configArd(ArdMask ardMask, unsigned int cnfBits)
+{
+  GpioExtMask  ioMask;
+
+  switch(ardMask)
+  {
+    case ArdA0A3:
+      ioMask.port = 0;
+      ioMask.pins = ArdA0Mask | ArdA1Mask | ArdA2Mask | ArdA3Mask;
+      break;
+
+    case ArdA4A7:
+      ioMask.port = 0;
+      ioMask.pins = ArdA4Mask | ArdA5Mask | ArdA6Mask | ArdA7Mask;
+      break;
+
+    case ArdA0A7:
+      ioMask.port = 0;
+      ioMask.pins = ArdA0Mask | ArdA1Mask | ArdA2Mask | ArdA3Mask |
+                    ArdA4Mask | ArdA5Mask | ArdA6Mask | ArdA7Mask;
+      break;
+
+    case ArdD2D5:
+      ioMask.port = 1;
+      ioMask.pins = ArdD2Mask | ArdD3Mask | ArdD4Mask | ArdD5Mask;
+      break;
+  }
+
+  return config(ioMask, cnfBits, NULL);
+}
+
+
+
+  // --------------------------------------------------------------------------
+  // Anwendungsfunktionen
+  // --------------------------------------------------------------------------
+  //
+void      nRF52840Gpio::read(GpioExtRefPtr ioRefPtr, GpioExtValPtr valPtr)
+{
+  gpioPtr = (nrfGpioPtr) ioRefPtr->ioPtr;
+  valPtr->value = gpioPtr->IN;
+}
+
+dword     nRF52840Gpio::readArd(ArdMask ardMask)
+{
+  dword   inVal;
+  dword   retVal;
+
+  retVal = 0;
+
+  switch(ardMask)
+  {
+    case ArdA0A3:
+      inVal = NrfGpioPtr0->IN;
+      if(inVal & ArdA0Mask) retVal |= 0x01;
+      if(inVal & ArdA1Mask) retVal |= 0x02;
+      if(inVal & ArdA2Mask) retVal |= 0x04;
+      if(inVal & ArdA3Mask) retVal |= 0x08;
+      break;
+
+    case ArdA4A7:
+      inVal = NrfGpioPtr0->IN;
+      if(inVal & ArdA4Mask) retVal |= 0x01;
+      if(inVal & ArdA5Mask) retVal |= 0x02;
+      if(inVal & ArdA6Mask) retVal |= 0x04;
+      if(inVal & ArdA7Mask) retVal |= 0x08;
+      break;
+
+    case ArdA0A7:
+      inVal = NrfGpioPtr0->IN;
+      if(inVal & ArdA0Mask) retVal |= 0x01;
+      if(inVal & ArdA1Mask) retVal |= 0x02;
+      if(inVal & ArdA2Mask) retVal |= 0x04;
+      if(inVal & ArdA3Mask) retVal |= 0x08;
+      if(inVal & ArdA4Mask) retVal |= 0x10;
+      if(inVal & ArdA5Mask) retVal |= 0x20;
+      if(inVal & ArdA6Mask) retVal |= 0x40;
+      if(inVal & ArdA7Mask) retVal |= 0x80;
+      break;
+
+    case ArdD2D5:
+      inVal = NrfGpioPtr1->IN;
+      if(inVal & ArdD2Mask) retVal |= 0x01;
+      if(inVal & ArdD3Mask) retVal |= 0x02;
+      if(inVal & ArdD4Mask) retVal |= 0x04;
+      if(inVal & ArdD5Mask) retVal |= 0x08;
+      break;
+  }
+
+  return(retVal);
+}
+
+
+void      nRF52840Gpio::write(GpioExtRefPtr refPtr, GpioExtValPtr valPtr)
+{
+  ((nrfGpioPtr) refPtr->ioPtr)->OUTSET = valPtr->value & refPtr->pins;
+  ((nrfGpioPtr) refPtr->ioPtr)->OUTCLR = ~valPtr->value & refPtr->pins;
+  if(refPtr->next == NULL) return;
+  ((nrfGpioPtr) refPtr->next->ioPtr)->OUTSET = valPtr->next->value & refPtr->next->pins;
+  ((nrfGpioPtr) refPtr->next->ioPtr)->OUTCLR = ~valPtr->next->value & refPtr->next->pins;
+}
+
+void      nRF52840Gpio::set(GpioExtRefPtr refPtr)
+{
+  ((nrfGpioPtr) refPtr->ioPtr)->OUTSET = refPtr->pins;
+  if(refPtr->next == NULL) return;
+  ((nrfGpioPtr) refPtr->next->ioPtr)->OUTSET = refPtr->next->pins;
+}
+
+void      nRF52840Gpio::clr(GpioExtRefPtr refPtr)
+{
+  ((nrfGpioPtr) refPtr->ioPtr)->OUTCLR = refPtr->pins;
+  if(refPtr->next == NULL) return;
+  ((nrfGpioPtr) refPtr->next->ioPtr)->OUTCLR = refPtr->next->pins;
+}
+
+
+
+void      nRF52840Gpio::writeArd(ArdMask ardMask, dword value)
+{
+  dword   set0 = 0, set1 = 0;
+  dword   clr0 = 0, clr1 = 0;
+
+  switch(ardMask)
+  {
+    case ArdA0A3:
+      if(value & 0x01) set0 |= ArdA0Mask;
+      else clr0 |= ArdA0Mask;
+      if(value & 0x02) set0 |= ArdA1Mask;
+      else clr0 |= ArdA1Mask;
+      if(value & 0x04) set0 |= ArdA2Mask;
+      else clr0 |= ArdA2Mask;
+      if(value & 0x08) set0 |= ArdA3Mask;
+      else clr0 |= ArdA3Mask;
+
+      NrfGpioPtr0->OUTSET = set0;
+      NrfGpioPtr0->OUTCLR = clr0;
+      break;
+
+    case ArdA4A7:
+      if(value & 0x01) set0 |= ArdA4Mask;
+      else clr0 |= ArdA4Mask;
+      if(value & 0x02) set0 |= ArdA5Mask;
+      else clr0 |= ArdA5Mask;
+      if(value & 0x04) set0 |= ArdA6Mask;
+      else clr0 |= ArdA6Mask;
+      if(value & 0x08) set0 |= ArdA7Mask;
+      else clr0 |= ArdA7Mask;
+
+      NrfGpioPtr0->OUTSET = set0;
+      NrfGpioPtr0->OUTCLR = clr0;
+      break;
+
+    case ArdA0A7:
+      if(value & 0x01) set0 |= ArdA0Mask;
+      else clr0 |= ArdA0Mask;
+      if(value & 0x02) set0 |= ArdA1Mask;
+      else clr0 |= ArdA1Mask;
+      if(value & 0x04) set0 |= ArdA2Mask;
+      else clr0 |= ArdA2Mask;
+      if(value & 0x08) set0 |= ArdA3Mask;
+      else clr0 |= ArdA3Mask;
+      if(value & 0x01) set0 |= ArdA4Mask;
+      else clr0 |= ArdA4Mask;
+      if(value & 0x02) set0 |= ArdA5Mask;
+      else clr0 |= ArdA5Mask;
+      if(value & 0x04) set0 |= ArdA6Mask;
+      else clr0 |= ArdA6Mask;
+      if(value & 0x08) set0 |= ArdA7Mask;
+      else clr0 |= ArdA7Mask;
+
+      NrfGpioPtr0->OUTSET = set0;
+      NrfGpioPtr0->OUTCLR = clr0;
+      break;
+
+    case ArdD2D5:
+      if(value & 0x01) set1 |= ArdD2Mask;
+      else clr1 |= ArdD2Mask;
+      if(value & 0x02) set1 |= ArdD3Mask;
+      else clr1 |= ArdD3Mask;
+      if(value & 0x04) set1 |= ArdD4Mask;
+      else clr1 |= ArdD4Mask;
+      if(value & 0x08) set1 |= ArdD5Mask;
+      else clr1 |= ArdD5Mask;
+
+      NrfGpioPtr1->OUTSET = set1;
+      NrfGpioPtr1->OUTCLR = clr1;
+      break;
+  }
+}
+
+
+  // ----------------------------------------------------------------------------
+  // Ereignisbearbeitung und Interrupts
+  // ----------------------------------------------------------------------------
+  //
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Gpio/nRF52840Gpio.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Gpio/nRF52840Gpio.h
new file mode 100644
index 0000000000000000000000000000000000000000..addf5ad533ddfecabd2c540824238c278a117998
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Gpio/nRF52840Gpio.h
@@ -0,0 +1,181 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Gpio.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+// Datum:   29. Juni 2021
+//
+
+#ifndef NRF52840GPIO_H
+#define NRF52840GPIO_H
+
+#include "Arduino.h"
+#include "arduinoDefs.h"
+#include "IntrfGpio.h"
+
+#ifndef nrfGpioDef
+// ----------------------------------------------------------------------------
+typedef struct _nrfGpio
+{
+  volatile  dword Reserve01;                // 000
+  volatile  dword OUT;                      // 004
+  volatile  dword OUTSET;                   // 008
+  volatile  dword OUTCLR;                   // 00C
+  volatile  dword IN;                       // 010
+  volatile  dword DIR;                      // 014
+  volatile  dword DIRSET;                   // 018
+  volatile  dword DIRCLR;                   // 01C
+  volatile  dword LATCH;                    // 020
+  volatile  dword DETECTMODE;               // 024
+  volatile  dword Reserve02[118];           // 026
+  volatile  dword PIN_CNF[32];              // 200
+} nrfGpio, *nrfGpioPtr;
+
+#define NrfGpioBase   0x50000000
+#define NrfGpioBase0  0x50000500
+#define NrfGpioPtr0   ((nrfGpioPtr) NrfGpioBase0)
+#define NrfGpioBase1  0x50000800
+#define NrfGpioPtr1   ((nrfGpioPtr) NrfGpioBase1)
+
+#define GpioPinCnf_DIR        ((dword) 0x00000001)
+
+#define GpioPinCnf_INPUT      ((dword) 0x00000001 << 1)
+
+#define GpioPinCnf_PULL(x)    ((dword) x << 2)
+#define GpioPullDown          1
+#define GpioPullUp            3
+
+#define GpioPinCnf_DRIVE(x)   ((dword) x << 8)
+#define GpioDriveS0S1         0
+#define GpioDriveH0S1         1
+#define GpioDriveS0H1         2
+#define GpioDriveH0H1         3
+#define GpioDriveD0S1         4
+#define GpioDriveD0H1         5
+#define GpioDriveS0D1         6
+#define GpioDriveH0D1         7
+
+#define GpioPinCnf_SENSE(x)   ((dword) x << 16)
+#define GpioSenseHigh         2
+#define GpioSenseLow          3
+
+#define nrfGpioDef
+// ----------------------------------------------------------------------------
+#endif
+
+#define P0(x) (x)
+#define P1(x) (32+x)
+
+#ifdef smnNANOBLE33
+// ----------------------------------------------------------------------------
+#define ArdA0Bit    4
+#define ArdA1Bit    5
+#define ArdA2Bit    30
+#define ArdA3Bit    29
+#define ArdA4Bit    31
+#define ArdA5Bit    2
+#define ArdA6Bit    28
+#define ArdA7Bit    3
+
+#define ArdA0   P0(4)
+#define ArdA1   P0(5)
+#define ArdA2   P0(30)
+#define ArdA3   P0(29)
+#define ArdA4   P0(31)
+#define ArdA5   P0(2)
+#define ArdA6   P0(28)
+#define ArdA7   P0(3)
+
+#define ArdA0Mask   (1 << 4)
+#define ArdA1Mask   (1 << 5)
+#define ArdA2Mask   (1 << 30)
+#define ArdA3Mask   (1 << 29)
+#define ArdA4Mask   (1 << 31)
+#define ArdA5Mask   (1 << 2)
+#define ArdA6Mask   (1 << 28)
+#define ArdA7Mask   (1 << 3)
+
+
+#define ArdD2       P1(11)
+#define ArdD3       P1(12)
+#define ArdD4       P1(15)
+#define ArdD5       P1(13)
+#define ArdD6       P0(14)
+#define ArdD7       P0(23)
+#define ArdD8       P1(21)
+#define ArdD9       P0(27)
+#define ArdD10      P1(2)
+#define ArdD11      P1(1)
+#define ArdD12      P1(8)
+#define ArdD13      P0(13)
+
+#define ArdD2Mask   (1 << 11)
+#define ArdD3Mask   (1 << 12)
+#define ArdD4Mask   (1 << 15)
+#define ArdD5Mask   (1 << 13)
+#define ArdD6Mask   (1 << 14)
+#define ArdD7Mask   (1 << 23)
+#define ArdD8Mask   (1 << 21)
+#define ArdD9Mask   (1 << 27)
+#define ArdD10Mask  (1 << 2)
+#define ArdD11Mask  (1 << 1)
+#define ArdD12Mask  (1 << 8)
+#define ArdD13Mask  (1 << 13)
+
+// ----------------------------------------------------------------------------
+#endif
+
+class nRF52840Gpio : IntrfGpio
+{
+private:
+  // --------------------------------------------------------------------------
+  // lokale Variablen
+  // --------------------------------------------------------------------------
+  //
+  nrfGpioPtr  gpioPtr;
+
+public:
+  // --------------------------------------------------------------------------
+  // Konstruktoren
+  // --------------------------------------------------------------------------
+  //
+  nRF52840Gpio();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  dword     getCnfValue(unsigned int cnfBits);
+  GpioError config(int nr, unsigned int cnfBits, GpioExtRefPtr refPtr);
+  GpioError config(int nrFrom, int nrTo, unsigned int cnfBits, GpioExtRefPtr refPtr);
+  GpioError config(GpioExtMask mask, unsigned int cnfBits, GpioExtRefPtr refPtr);
+
+  GpioError configArd(ArdMask ardMask, unsigned int cnfBits);
+
+  // --------------------------------------------------------------------------
+  // Anwendungsfunktionen
+  // --------------------------------------------------------------------------
+  //
+  void      read(GpioExtRefPtr ioRef, GpioExtValPtr valPtr);
+  dword     readArd(ArdMask ardMask);
+
+  void      write(GpioExtRefPtr refPtr, GpioExtValPtr valPtr);
+  void      writeArd(ArdMask ardMask, dword value);
+  void      set(GpioExtRefPtr refPtr);
+  void      clr(GpioExtRefPtr refPtr);
+
+  // ----------------------------------------------------------------------------
+  // Ereignisbearbeitung und Interrupts
+  // ----------------------------------------------------------------------------
+  //
+
+  // --------------------------------------------------------------------------
+  // Debugging und globale Variablen
+  // --------------------------------------------------------------------------
+  //
+
+};
+
+#endif //NRF52840GPIO_H
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Radio/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Radio/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..a18f8f0f08e3ee567cac8bd59e8774c14b3effe7
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Radio/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "nRF52840Radio",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Radio/nRF52840Radio.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Radio/nRF52840Radio.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5639480f98f812bc2170a24674d34534dd95d5e6
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Radio/nRF52840Radio.cpp
@@ -0,0 +1,943 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Radio.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#include "Arduino.h"
+#include "nRF52840Radio.h"
+#include <string.h>
+
+// ----------------------------------------------------------------------------
+// Initialisierungen
+// ----------------------------------------------------------------------------
+
+nRF52840Radio::nRF52840Radio()
+{
+  NrfRadioPtr->TASKS_DISABLE;   // Sender/Empfänger abgeschaltet
+#ifdef nrfPowerDCDCEN
+  *nrfPowerDCDCEN = 1;
+#endif
+#ifdef nrfClockTASKS_HFCLKSTART
+  *nrfClockTASKS_HFCLKSTART = 1;
+#endif
+  NrfRadioPtr->POWER = 0;
+  NrfRadioPtr->POWER = 1;
+
+  irqCounter = 0;
+  trfMode = txmBase;
+  statisticPtr = NULL;
+  recMode = true;
+  pmPtr = (bcPduPtr) pduMem;
+  psPtr = (bcPduPtr) pduSentS;
+  pePtr = (bcPduPtr) pduSentE;
+  eadM = false;
+  nakM = false;
+  comFin = false;
+  comError = false;
+  newValues = false;
+
+  memset(statList,0,NrOfTxModes * sizeof(TxStatistics));
+}
+
+// ----------------------------------------------------------------------------
+// Konfiguration
+// ----------------------------------------------------------------------------
+
+// Allgemeine Vorbereitungen
+//
+void  nRF52840Radio::begin()
+{
+  instPtr0 = this;      // Verzweigung im statischen Bereich setzen
+
+  NrfRadioPtr->INTENCLR = 0xFFFFFFFF;   // Vorsichtshalber keine Interrupts
+
+  // Interruptvektor setzen
+  //
+  __NVIC_SetVector((IRQn_Type) 1, (dword) nRF52840Radio::irqHandler0);
+  __NVIC_SetPriority((IRQn_Type) 1, 1);
+  __NVIC_EnableIRQ((IRQn_Type) 1);
+}
+
+// Setzen der Zugriffsadresse
+//
+void  nRF52840Radio::setAccessAddress(dword addr)
+{
+  dword prefix = addr >> 24;
+  dword base = addr << 8;
+
+  cfgData.base0   = NrfRadioPtr->BASE0        = base;
+  cfgData.prefix0 = NrfRadioPtr->PREFIX0      = prefix;
+  cfgData.txAddr  = NrfRadioPtr->TXADDRESS    = 0;
+  cfgData.rxAddr  = NrfRadioPtr->RXADDRESSES  = 0x01;
+}
+
+// Telegrammparameter setzen
+//
+void  nRF52840Radio::setPacketParms(blePduType type)
+{
+  switch(type)
+  {
+    case bptAdv:
+      cfgData.pCnf0     = NrfRadioPtr->PCNF0        = PCNF0_LFLEN(8) | PCNF0_S0LEN(1) | PCNF0_S1LEN(0);
+      cfgData.pCnf1     = NrfRadioPtr->PCNF1        = PCNF1_MAXLEN(42) | PCNF1_BALEN(3) | PCNF1_WHITEEN(1);
+      cfgData.modeCnf0  = NrfRadioPtr->MODECNF0     = 1;
+      cfgData.crcCnf    = NrfRadioPtr->CRCCNF       = CRCCNF_LEN(3) | CRCCNF_SKIPADDR(1);
+      cfgData.crcPoly   = NrfRadioPtr->CRCPOLY      = PolynomCRC;
+      cfgData.crcInit   = NrfRadioPtr->CRCINIT      = AdvStartCRC;
+      cfgData.packetPtr = NrfRadioPtr->PACKETPTR    = (dword) pduMem;
+      cfgData.mode      = NrfRadioPtr->MODE         = 3;
+      cfgData.dacnf     = NrfRadioPtr->DACNF        = 0x0FF00;
+      break;
+
+    case bptAux:
+      break;
+  }
+}
+
+
+// ----------------------------------------------------------------------------
+// Steuerfunktionen und gezielte Prozessorzugriffe
+// ----------------------------------------------------------------------------
+
+// Schalten des Bewerbungskanals
+//
+void nRF52840Radio::setChannel(int nr)
+{
+  cfgData.frequency = NrfRadioPtr->FREQUENCY = channelList[nr].freq;
+  cfgData.whiteInit = NrfRadioPtr->DATAWHITEIV = channelList[nr].idx;
+}
+
+// ----------------------------------------------------------------------------
+//                      S e n d e n
+// ----------------------------------------------------------------------------
+
+// Einstellen der Sendeleistung
+//
+void  nRF52840Radio::setPower(int DBm)
+{
+  cfgData.txPower = NrfRadioPtr->TXPOWER = DBm;
+}
+// Senden eines Telegramms
+// Es wird davon ausgeganen, das der Radio-Zustand = DISABLED ist
+//
+int nRF52840Radio::sendSync(bcPduPtr inPduPtr, TxStatePtr refState)
+{
+  int   retv = 0;
+  NrfRadioPtr->INTENCLR = 0xFFFFFFFF;
+  NrfRadioPtr->EVENTS_READY = 0;
+  NrfRadioPtr->EVENTS_END = 0;
+  memcpy((void *)pduMem, (void *)inPduPtr, sizeof(bcPdu));  // Daten kopieren
+  if(refState != NULL)
+    refState->prgLoopPrep = retv = 3 + sizeof(bcPdu);
+  NrfRadioPtr->TASKS_TXEN = 1;                  // Starten des Anlaufes
+  while(NrfRadioPtr->EVENTS_READY != 1) retv++; // Warten bis angelaufen
+  if(refState != NULL)
+    refState->evtLoopRampUp = retv - 3;
+  NrfRadioPtr->TASKS_START = 1;                 // Starten des Sendevorgangs
+  while(NrfRadioPtr->EVENTS_END != 1) retv++;   // Warten bis gesendet
+  NrfRadioPtr->TASKS_DISABLE = 1;               // Sender abschalten
+  if(refState != NULL)
+  {
+    refState->evtLoopTrans = retv - refState->evtLoopRampUp;
+    refState->txBufferPtr = pduMem;
+  }
+  return(retv);
+}
+
+void  nRF52840Radio::send(bcPduPtr inPduPtr, TxMode txMode)
+{
+  send(inPduPtr, NULL, txMode, false);
+}
+
+void  nRF52840Radio::send(bcPduPtr inPduPtrE, bcPduPtr inPduPtrS, TxMode txMode, bool inNewValues)
+{
+  NrfRadioPtr->INTENCLR         = 0xFFFFFFFF;
+  NrfRadioPtr->EVENTS_READY     = 0;
+  NrfRadioPtr->EVENTS_END       = 0;
+  NrfRadioPtr->EVENTS_DISABLED  = 0;
+  NrfRadioPtr->EVENTS_RXREADY   = 0;
+  NrfRadioPtr->EVENTS_TXREADY   = 0;
+  NrfRadioPtr->EVENTS_ADDRESS   = 0;
+
+  // TODO
+  // Das muss Alles noch einmal überarbeitet werden.
+  // Hier stecken zu viele Redundanzen drin, Altlast aus diversen Tests mit der Hardware
+
+  memcpy((void *)pduMem, (void *)inPduPtrE, sizeof(bcPdu));    // Daten in Funkpuffer kopieren
+
+  memcpy((void *)pduSentE, (void *)inPduPtrE, sizeof(bcPdu));  // Daten in extra Puffer kopieren
+  // Die übergebenen Daten werden in einen Extrapuffer kopiert zur Entkopplung für eventuelle
+  // lokale Modifikationen
+
+  if(inPduPtrS != NULL)                             // Falls Daten für eine Antwort gegeben sind
+    memcpy((void *)pduSentS, (void *)inPduPtrS, sizeof(bcPdu));// Daten in extra Puffer kopieren
+  // Die übergebenen Daten werden in einen Extrapuffer kopiert zur Entkopplung für eventuelle
+  // lokale Modifikationen
+
+  comFin    = false;
+  comError  = false;
+  newValues = inNewValues;
+
+  trfMode = txMode;
+
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+  statisticPtr = &statList[(int) txMode];
+  statisticPtr->mode = txMode;
+  memset(statisticPtr->memDumpRec,0,16);
+  memset(statisticPtr->memDumpSnd,0,16);
+#endif
+
+  switch(txMode)
+  {
+    case txmPoll:
+      recMode = false;
+      NrfRadioPtr->SHORTS = NrfScTXREADY_START | NrfScEND_DISABLE | NrfScDISABLED_RXEN | NrfScRXREADY_START;
+      NrfRadioPtr->INTENSET = NrfIntRXREADY;
+      NrfRadioPtr->TASKS_TXEN = 1;
+      break;
+
+    case txmResp:
+    case txmRespE:
+      recMode = true;
+      NrfRadioPtr->SHORTS = NrfScREADY_START;
+      NrfRadioPtr->INTENSET = NrfIntEND;
+      NrfRadioPtr->TASKS_RXEN = 1;
+      break;
+
+    case txmBase:
+      NrfRadioPtr->SHORTS = NrfScREADY_START | NrfScEND_DISABLE;
+      NrfRadioPtr->TASKS_TXEN = 1;
+      break;
+
+    case txmRepStart:
+      NrfRadioPtr->SHORTS = NrfScREADY_START;
+      NrfRadioPtr->TASKS_TXEN = 1;
+      break;
+
+    case txmRepCont:
+      NrfRadioPtr->SHORTS = 0;
+      NrfRadioPtr->TASKS_START = 1;
+      break;
+
+    case txmRepEnd:
+      NrfRadioPtr->SHORTS = NrfScEND_DISABLE;
+      NrfRadioPtr->TASKS_START = 1;
+      break;
+
+    case txmReadPrep:
+      NrfRadioPtr->SHORTS = NrfScTXREADY_START | NrfScEND_DISABLE | NrfScDISABLED_RXEN;
+      NrfRadioPtr->TASKS_TXEN = 1;
+      break;
+
+    case txmReadS:
+      NrfRadioPtr->SHORTS = NrfScTXREADY_START | NrfScEND_DISABLE | NrfScDISABLED_RXEN | NrfScRXREADY_START;
+      NrfRadioPtr->INTENSET = NrfIntADDRESS;
+      NrfRadioPtr->TASKS_TXEN = 1;
+      break;
+
+    case txmRead:
+      NrfRadioPtr->SHORTS = NrfScTXREADY_START | NrfScEND_DISABLE | NrfScDISABLED_RXEN | NrfScRXREADY_START;
+      NrfRadioPtr->INTENSET = NrfIntADDRESS | NrfIntEND;
+      NrfRadioPtr->TASKS_TXEN = 1;
+      break;
+  }
+}
+
+void nRF52840Radio::disable(TxMode txMode)
+{
+  switch(txMode)
+   {
+     case txmBase:
+       break;
+
+     case txmRepStart:
+       break;
+
+     case txmRepCont:
+       break;
+
+     case txmRepEnd:
+       break;
+
+     case txmReadPrep:
+       break;
+
+     case txmRead:
+       NrfRadioPtr->TASKS_DISABLE = 1;
+       break;
+
+     case txmPoll:
+       NrfRadioPtr->INTENCLR = 0xFFFFFFFF;
+       NrfRadioPtr->EVENTS_DISABLED = 0;
+       NrfRadioPtr->TASKS_DISABLE = 1;
+       break;
+
+     case txmResp:
+     case txmRespE:
+       NrfRadioPtr->TASKS_DISABLE = 1;
+       break;
+   }
+  NrfRadioPtr->SHORTS = 0;
+}
+
+bool nRF52840Radio::disabled(TxMode txMode)
+{
+  bool retv = false;
+
+  switch(txMode)
+   {
+     case txmBase:
+       break;
+
+     case txmRepStart:
+       break;
+
+     case txmRepCont:
+       break;
+
+     case txmRepEnd:
+       break;
+
+     case txmReadPrep:
+       break;
+
+     case txmRead:
+     case txmPoll:
+       if(NrfRadioPtr->EVENTS_DISABLED == 1)
+       {
+         NrfRadioPtr->EVENTS_DISABLED = 0;
+         retv = true;
+       }
+       else
+       {
+         if(NrfRadioPtr->STATE == NrfStDISABLED)
+           retv = true;
+       }
+       break;
+
+     case txmResp:
+     case txmRespE:
+       if(NrfRadioPtr->EVENTS_DISABLED == 1)
+       {
+         NrfRadioPtr->EVENTS_DISABLED = 0;
+         retv = true;
+       }
+       else
+       {
+         if(NrfRadioPtr->STATE == NrfStDISABLED)
+           retv = true;
+       }
+       break;
+   }
+  return(retv);
+}
+
+bool nRF52840Radio::fin(TxMode txMode, bool *crcErr)
+{
+  bool retv = false;
+
+  *crcErr = comError;
+
+  switch(txMode)
+   {
+     case txmBase:
+       break;
+
+     case txmRepStart:
+       break;
+
+     case txmRepCont:
+       break;
+
+     case txmRepEnd:
+       break;
+
+     case txmReadPrep:
+       break;
+
+     case txmReadS:
+       retv = comFin;
+       break;
+
+     case txmRead:
+       /*
+       if(NrfRadioPtr->EVENTS_END == 1)
+       {
+         NrfRadioPtr->EVENTS_END = 0;
+         retv = true;
+       }
+       else
+       {
+         if(NrfRadioPtr->STATE == NrfStRXIDLE)
+           retv = true;
+       }
+       */
+       retv = comFin;
+       break;
+
+     case txmPoll:
+       retv = comFin;
+       break;
+
+     case txmResp:
+       retv = comFin;
+       break;
+
+     case txmRespE:
+       if(NrfRadioPtr->STATE == NrfStDISABLED && !recMode)
+         retv = true;
+       break;
+   }
+  return(retv);
+}
+
+void nRF52840Radio::cont(TxMode txMode)
+{
+  switch(txMode)
+   {
+     case txmBase:
+       break;
+
+     case txmRepStart:
+       break;
+
+     case txmRepCont:
+       break;
+
+     case txmRepEnd:
+       break;
+
+     case txmReadPrep:
+       break;
+
+     case txmRead:
+       comFin = false;
+       NrfRadioPtr->TASKS_START = 1;
+       break;
+   }
+}
+
+int   nRF52840Radio::getRecData(bcPduPtr data, TxMode txMode, int max)
+{
+  byte *bPtr = (byte *) data;
+  int retv = 0;
+
+  switch(txMode)
+  {
+    case txmResp:
+      data->head = pduSentE[0];
+      retv = data->len  = pduSentE[1];
+
+      for(int i = 2; i < (retv + 2); i++)
+      {
+        if(i == max) break;
+        bPtr[i] = pduSentE[i];
+      }
+
+      break;
+
+    case txmBase:
+      break;
+
+    case txmRepStart:
+      break;
+
+    case txmRepCont:
+      break;
+
+    case txmRepEnd:
+      break;
+
+    case txmReadPrep:
+      break;
+
+    case txmRead:
+      break;
+  }
+
+
+  return(retv);
+}
+
+
+
+// ----------------------------------------------------------------------------
+//                      E m p f a n g e n
+// ----------------------------------------------------------------------------
+
+// Starten des Datenempfangs
+//
+int nRF52840Radio::startRec()
+{
+  int   retv;
+  NrfRadioPtr->INTENCLR = 0xFFFFFFFF;
+  NrfRadioPtr->EVENTS_READY = 0;
+  NrfRadioPtr->EVENTS_END = 0;
+  NrfRadioPtr->EVENTS_ADDRESS = 0;
+  NrfRadioPtr->EVENTS_PAYLOAD = 0;
+  NrfRadioPtr->EVENTS_CRCOK = 0;
+  NrfRadioPtr->EVENTS_CRCERROR = 0;
+  NrfRadioPtr->TASKS_RXEN = 1;                  // Anlauf Empfänger starten
+  retv = 8;
+  while(NrfRadioPtr->EVENTS_READY != 1) retv++; // Warten bis angelaufen
+  NrfRadioPtr->TASKS_START = 1;                 // Starten des Empfangs
+  return(retv + 1);
+}
+
+// Fortsetzen des Datenempfangs
+//
+int nRF52840Radio::contRec()
+{
+  NrfRadioPtr->EVENTS_END = 0;
+  NrfRadioPtr->EVENTS_ADDRESS = 0;
+  NrfRadioPtr->EVENTS_PAYLOAD = 0;
+  NrfRadioPtr->EVENTS_CRCOK = 0;
+  NrfRadioPtr->EVENTS_CRCERROR = 0;
+  NrfRadioPtr->TASKS_START = 1;                 // Starten des Empfangs
+  return(6);
+}
+
+// Beenden des Datenempfangs
+//
+int nRF52840Radio::endRec()
+{
+  int   retv;
+  NrfRadioPtr->EVENTS_DISABLED = 0;
+  NrfRadioPtr->EVENTS_END = 0;
+  NrfRadioPtr->EVENTS_ADDRESS = 0;
+  NrfRadioPtr->EVENTS_PAYLOAD = 0;
+  NrfRadioPtr->EVENTS_CRCOK = 0;
+  NrfRadioPtr->EVENTS_CRCERROR = 0;
+  NrfRadioPtr->TASKS_DISABLE = 1;               // Anlauf Empfänger beenden
+  retv = 7;
+  while(NrfRadioPtr->EVENTS_DISABLED != 1) retv++; // Warten bis abgelaufen
+  return(retv);
+}
+
+// Empfangszustand abfragen
+//
+int nRF52840Radio::checkRec()
+{
+  int retv = 0;
+
+  if(NrfRadioPtr->EVENTS_ADDRESS != 0)
+    retv |= RECSTAT_ADDRESS;
+
+  if(NrfRadioPtr->EVENTS_PAYLOAD != 0)
+    retv |= RECSTAT_PAYLOAD;
+
+  if(NrfRadioPtr->EVENTS_END != 0)
+    retv |= RECSTAT_END;
+
+  if(NrfRadioPtr->CRCSTATUS != 0)
+    retv |= RECSTAT_CRCOK;
+
+  if(NrfRadioPtr->EVENTS_DISABLED != 0)
+    retv |= RECSTAT_DISABLED;
+
+  return(retv);
+}
+
+int   nRF52840Radio::getRecData(bcPduPtr data, int max)
+{
+  int retv;
+  byte *bPtr = (byte *) data;
+
+  data->head = pduMem[0];
+  retv = data->len  = pduMem[1];
+
+  for(int i = 2; i < (retv + 2); i++)
+  {
+    if(i == max) break;
+    bPtr[i] = pduMem[i];
+  }
+
+  return(retv);
+}
+
+// ----------------------------------------------------------------------------
+// Interruptverarbeitung
+// ----------------------------------------------------------------------------
+//
+
+nRF52840Radio *nRF52840Radio::instPtr0 = NULL;
+
+void nRF52840Radio::irqHandler0()
+{
+  if(instPtr0 == NULL) return;
+  instPtr0->irqCounter++;
+  instPtr0->irqHandler();
+}
+
+void nRF52840Radio::irqHandler()
+{
+  statisticPtr->interrupts++;
+
+  switch(trfMode)
+  {
+    // ------------------------------------------------------------------------
+    case txmRead:               // Empty Polling für Master
+    // ------------------------------------------------------------------------
+
+      if((NrfRadioPtr->STATE & 0x08) != 0)  // Noch im Sendemodus
+      { // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_ADDRESS == 1)  // AC-Adr gesendet
+        {
+          NrfRadioPtr->EVENTS_ADDRESS = 0;    // nur quittieren
+        }
+
+        if(NrfRadioPtr->EVENTS_END == 1)      // Senden fertig
+        {
+          NrfRadioPtr->EVENTS_END = 0;        // nur quittieren
+          statisticPtr->sendings++;
+          memcpy(statisticPtr->memDumpSnd,pduMem,8);
+        }
+      }
+      else                                  // im Empfangsmodus
+      { // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_ADDRESS == 1)  // AC-Adr empfangen
+        {
+          NrfRadioPtr->EVENTS_ADDRESS = 0;    // quittieren
+          NrfRadioPtr->SHORTS = 0;            // Direktverbindungen löschen
+        }
+
+        if(NrfRadioPtr->EVENTS_END == 1)      // Empfang fertig
+        {
+          NrfRadioPtr->EVENTS_END = 0;        // nur quittieren
+          comFin = true;
+          statisticPtr->recs++;
+          memcpy(statisticPtr->memDumpRec,pduMem,8);
+        }
+      }
+
+      break;
+
+    // ------------------------------------------------------------------------
+    case txmPoll:     // Empty Polling und Datenempfang für Master -> DISABLED
+    // ------------------------------------------------------------------------
+      if(NrfRadioPtr->EVENTS_RXREADY == 1)    // Empfang aktiviert
+      {
+        NrfRadioPtr->EVENTS_RXREADY = 0;      // Int quittieren
+
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+        statisticPtr->sendings++;
+        memcpy(statisticPtr->memDumpSnd,pduMem,8);
+#endif
+        NrfRadioPtr->EVENTS_DISABLED = 0;     // Neu erwartet, zurücksetzen
+        NrfRadioPtr->SHORTS = NrfScEND_DISABLE | NrfScRXREADY_START;
+        NrfRadioPtr->INTENSET = NrfIntDISABLED;
+        recMode = true;
+      }
+
+      if(NrfRadioPtr->EVENTS_DISABLED == 1)   // Empfang beendet
+      {
+        NrfRadioPtr->EVENTS_DISABLED = 0;     // Int quittieren
+        comFin = true;
+        if(NrfRadioPtr->CRCSTATUS == 0)
+          comError = true;
+
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+        statisticPtr->recs++;
+        if(comError) statisticPtr->crcErrors++;
+        memcpy(statisticPtr->memDumpRec,pduMem,16);
+        if(!recMode)
+        {
+          // Das darf nicht passieren, sonst neue Int-Verarbeitung erforderlich
+          statisticPtr->intErrors++;
+        }
+#endif
+      }
+      break;
+
+    // ------------------------------------------------------------------------
+    case txmReadS:              // Datenübertragung für Master (Slave->Master)
+    // ------------------------------------------------------------------------
+
+      if(NrfRadioPtr->EVENTS_ADDRESS == 1)    // AC-Adr gesendet
+      {
+        NrfRadioPtr->EVENTS_ADDRESS = 0;      // quittieren
+
+        if((NrfRadioPtr->STATE & 0x08) != 0)  // im Sendezustand
+          break;                              // nichts weiter
+        // --------------------------------------------------------------------
+        else                                  // Im Empfangszustand
+        {
+          NrfRadioPtr->SHORTS = NrfScEND_DISABLE; // automatisch abschalten
+          NrfRadioPtr->INTENCLR = 0xFFFFFFFF;     // und nur noch DISABLED
+          NrfRadioPtr->INTENSET = NrfIntDISABLED; // Interrupt freischalten
+        }
+      }
+
+      if(NrfRadioPtr->EVENTS_DISABLED == 1)   // Übertragung fertig
+      {
+        NrfRadioPtr->EVENTS_DISABLED = 0;     // quittieren
+        comFin = true;
+      }
+
+
+      break;
+
+    // ----------------------------------------------------------------------
+    case txmRespE:              // Empty Polling für Slave
+    // ----------------------------------------------------------------------
+
+        // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_END == 1)        // Übertragung beendet
+        // --------------------------------------------------------------------
+        {
+          NrfRadioPtr->EVENTS_END = 0;          // Event quittieren
+
+          if(recMode)                           // im Empfangsmodus
+          { // ----------------------------------------------------------------
+            NrfRadioPtr->SHORTS = 0;            // keine direkte Kopplung mehr
+
+            statisticPtr->recs++;
+            memcpy(statisticPtr->memDumpRec,pduMem,8);
+
+            // ----------------------------------------------------------------
+            // Reaktion
+            // ----------------------------------------------------------------
+            //
+            if((pduSentE[5] == pduMem[5]) && (pduSentE[6] == pduMem[6]) && (pduSentE[7] == pduMem[7]))
+            {
+              // Die richtige Protokollumgebung (z.B. Soaap)
+              //
+              if(pduSentE[2] != pduMem[2])
+              {
+                // aber die falsche Adresse
+                // Datenempfang fortsetzen
+                statisticPtr->wrongs++;
+                NrfRadioPtr->TASKS_START = 1;
+              }
+              else
+              { // richtige Adresse, Antwort schicken
+                // ------------------------------------------------------------
+                statisticPtr->pollNaks++;
+
+                // zunächst alle Funk-Interrupts sperren
+                NrfRadioPtr->INTENCLR = 0xFFFFFFFF;
+
+                // Interrupt freigeben für "Abgeschaltet"
+                NrfRadioPtr->INTENSET = NrfIntDISABLED;
+
+                // Empfangsbetrieb abschalten
+                NrfRadioPtr->TASKS_DISABLE = 1;
+              }
+
+            }
+            else
+            {
+              // Fremde Umgebung (nicht akzeptierte PDU)
+              // Datenempfang fortsetzen
+              statisticPtr->aliens++;
+              NrfRadioPtr->TASKS_START = 1;
+            }
+          }
+          else                                      // im Sendemodus
+          { // ----------------------------------------------------------------
+
+
+          }
+        }
+
+        // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_DISABLED == 1)       // ausgeschaltet
+        // --------------------------------------------------------------------
+        {
+          NrfRadioPtr->EVENTS_DISABLED = 0;         // quittieren
+
+          if(recMode)
+          {
+            // zunächst alle Funk-Interrupts sperren
+            NrfRadioPtr->INTENCLR = 0xFFFFFFFF;
+
+            // Interrupt freigeben für "Abgeschaltet"
+            NrfRadioPtr->INTENSET = NrfIntDISABLED;
+
+            // Kopplung automatisch starten und abschalten nach Ende
+            NrfRadioPtr->SHORTS = NrfScREADY_START | NrfScEND_DISABLE;
+
+            NrfRadioPtr->TASKS_TXEN = 1;             // Sender einschalten
+            recMode = false;
+
+            // Daten in Funkpuffer kopieren
+            memcpy((void *)pduMem, (void *)pduSentE, sizeof(bcPdu));
+          }
+          else
+          {
+            NrfRadioPtr->SHORTS = 0;
+            statisticPtr->sendings++;
+          }
+        }
+
+        break;
+
+      // ----------------------------------------------------------------------
+      case txmResp:               // Datenübertragung Slave
+      // ----------------------------------------------------------------------
+
+        // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_END == 1)        // Übertragung beendet
+                                                // Polling-Daten empfangen
+        // --------------------------------------------------------------------
+        {
+          NrfRadioPtr->EVENTS_END = 0;          // Event quittieren
+
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+          statisticPtr->recs++;
+          memcpy(statisticPtr->memDumpRec,pduMem,8);
+#endif
+
+          if((pduSentE[5] != pduMem[5]) || (pduSentE[6] != pduMem[6]) || (pduSentE[7] != pduMem[7]))
+          {
+            // Das empfangene Telegramm gehört nicht zum eigenen Netzwerk
+            //
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+            statisticPtr->aliens++;
+#endif
+            NrfRadioPtr->SHORTS = 0;            // Keine Direktverbindungen
+            NrfRadioPtr->TASKS_START = 1;       // Datenempfang fortsetzen
+            break;
+          }
+
+          if((pduSentE[2] != pduMem[2]) || ((pduSentE[3] & 0x3F) != (pduMem[3] & 0x3F)))
+          {
+            // Das empfangene Telegramm ist für einen anderen Slave oder Bereich
+            //
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+            statisticPtr->wrongs++;
+#endif
+            NrfRadioPtr->SHORTS = 0;            // Keine Direktverbindungen
+            NrfRadioPtr->TASKS_START = 1;       // Datenempfang fortsetzen
+            break;
+            // TODO
+            // Hier wird das Protokoll noch erweitert zum Belauschen anderer Slaves
+          }
+
+          eadM = ((pduMem[3] & SOAAP_EADR) != 0); // Merker für Empfangspolling
+          nakM = ((pduMem[3] & SOAAP_NAK) != 0);  // Merker für NAK-Polling
+
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+          if(nakM)
+            statisticPtr->pollNaks++;
+          else
+            statisticPtr->pollAcks++;
+#endif
+          if(eadM)
+          {
+            // Empfangsaufforderung
+            // Eadr-Nak-Daten in Funkpuffer kopieren
+            //
+            memcpy((void *)pduMem, (void *)pduSentE, sizeof(bcPdu));
+          }
+          else
+          {
+            // Sendeaufforderung
+            // Polling-Steuerdaten in Empfangspuffer und
+            // Sadr-Ack-Daten in Funkpuffer kopieren
+            //
+            memcpy((void *)pduSentE, (void *)pduMem, sizeof(bcPdu));
+            memcpy((void *)pduMem, (void *)pduSentS, sizeof(bcPdu));
+          }
+
+          // Setzen der Direktverbinder auf vollständigen Durchlauf bis Ende der Sendung
+          // ACHTUNG! Freigegebene Interrupts treten auf, auch wenn sie zu einer
+          //          Direktverbindung gehören.
+          //
+          NrfRadioPtr->SHORTS = NrfScDISABLED_TXEN | NrfScREADY_START | NrfScEND_DISABLE;
+          NrfRadioPtr->EVENTS_READY = 0;        // Evt. hängendes Ereignis löschen
+          NrfRadioPtr->INTENSET = NrfIntREADY;  // Int zur Vorbereitung des Sendeabschlusses
+          NrfRadioPtr->TASKS_DISABLE = 1;       // Abschalten des Empfangsbetriebs
+          break;
+        }
+
+        // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_READY == 1)      // Bereit zum Senden
+        // --------------------------------------------------------------------
+        {
+          NrfRadioPtr->EVENTS_READY = 0;        // Event quittieren
+
+          // Vorbereiten auf das Ende der Sendung mit Abschalten
+          // NrfScREADY_START | NrfScEND_DISABLE sind weiter wirksam
+          //
+          NrfRadioPtr->SHORTS = NrfScREADY_START | NrfScEND_DISABLE;
+          NrfRadioPtr->EVENTS_DISABLED = 0;       // Evt. hängendes Ereignis löschen
+          recMode = false;
+          NrfRadioPtr->INTENSET = NrfIntDISABLED; // Int zum Sendeabschluss
+          break;
+        }
+
+        // --------------------------------------------------------------------
+        if(NrfRadioPtr->EVENTS_DISABLED == 1)   // Sendevorgang beendet
+        // --------------------------------------------------------------------
+        {
+          NrfRadioPtr->EVENTS_DISABLED = 0;     // Event quittieren
+          comFin = true;
+#if (defined smnDEBUG  && defined nRF52840RadioDEB)
+          statisticPtr->sendings++;
+          memcpy(statisticPtr->memDumpSnd,pduMem,16);
+#endif
+        }
+
+        break;
+
+  }
+}
+
+// --------------------------------------------------------------------------
+// Datenzugriffe
+// --------------------------------------------------------------------------
+//
+int   nRF52840Radio::getStatistics(TxStatisticsPtr dest)
+{
+  int retv = 0;
+
+  *dest = *statisticPtr;
+  return(retv);
+}
+
+int nRF52840Radio::getState()
+{
+  return(NrfRadioPtr->STATE);
+}
+
+// ----------------------------------------------------------------------------
+//                      D e b u g - H i l f e n
+// ----------------------------------------------------------------------------
+//
+int   nRF52840Radio::getPduMem(byte *dest, int start, int end)
+{
+  int i,j;
+
+  j = 0;
+
+  for(i = start; i < end; i++)
+  {
+    dest[j++] = pduMem[i];
+  }
+  return(j);
+}
+
+int   nRF52840Radio::getPduSent(byte *dest, int start, int end)
+{
+  int i,j;
+
+  j = 0;
+
+  for(i = start; i < end; i++)
+  {
+    dest[j++] = pduSentE[i];
+  }
+  return(j);
+}
+
+
+
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Radio/nRF52840Radio.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Radio/nRF52840Radio.h
new file mode 100644
index 0000000000000000000000000000000000000000..0e61246043b8b3bd78928b3e33e31f81e180d011
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Radio/nRF52840Radio.h
@@ -0,0 +1,313 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Radio.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#ifndef NRF52840RADIO_H
+#define NRF52840RADIO_H
+
+#include "arduinoDefs.h"
+#include "bleSpec.h"
+#include "IntrfRadio.h"
+
+#define nRF52840RadioDEB
+
+// ----------------------------------------------------------------------------
+
+typedef struct _NRF_RADIO_Type
+{
+  volatile  dword  TASKS_TXEN;
+  volatile  dword  TASKS_RXEN;
+  volatile  dword  TASKS_START;
+  volatile  dword  TASKS_STOP;
+  volatile  dword  TASKS_DISABLE;
+  volatile  dword  TASKS_RSSISTART;
+  volatile  dword  TASKS_RSSISTOP;
+  volatile  dword  TASKS_BCSTART;
+  volatile  dword  TASKS_BCSTOP;
+  volatile  dword  TASKS_EDSTART;
+  volatile  dword  TASKS_EDSTOP;
+  volatile  dword  TASKS_CCASTART;
+  volatile  dword  TASKS_CCASTOP;
+  volatile  dword  RESERVED0[51];
+  volatile  dword  EVENTS_READY;
+  volatile  dword  EVENTS_ADDRESS;
+  volatile  dword  EVENTS_PAYLOAD;
+  volatile  dword  EVENTS_END;
+  volatile  dword  EVENTS_DISABLED;
+  volatile  dword  EVENTS_DEVMATCH;
+  volatile  dword  EVENTS_DEVMISS;
+  volatile  dword  EVENTS_RSSIEND;
+  volatile  dword  RESERVED1[2];
+  volatile  dword  EVENTS_BCMATCH;
+  volatile  dword  RESERVED2;
+  volatile  dword  EVENTS_CRCOK;
+  volatile  dword  EVENTS_CRCERROR;
+  volatile  dword  EVENTS_FRAMESTART;
+  volatile  dword  EVENTS_EDEND;
+  volatile  dword  EVENTS_EDSTOPPED;
+  volatile  dword  EVENTS_CCAIDLE;
+  volatile  dword  EVENTS_CCABUSY;
+  volatile  dword  EVENTS_CCASTOPPED;
+  volatile  dword  EVENTS_RATEBOOST;
+  volatile  dword  EVENTS_TXREADY;
+  volatile  dword  EVENTS_RXREADY;
+  volatile  dword  EVENTS_MHRMATCH;
+  volatile  dword  RESERVED3[2];
+  volatile  dword  EVENTS_SYNC;
+  volatile  dword  EVENTS_PHYEND;
+  volatile  dword  RESERVED4[36];
+  volatile  dword  SHORTS;
+  volatile  dword  RESERVED5[64];
+  volatile  dword  INTENSET;
+  volatile  dword  INTENCLR;
+  volatile  dword  RESERVED6[61];
+  volatile  dword  CRCSTATUS;
+  volatile  dword  RESERVED7;
+  volatile  dword  RXMATCH;
+  volatile  dword  RXCRC;
+  volatile  dword  DAI;
+  volatile  dword  PDUSTAT;
+  volatile  dword  RESERVED8[59];
+  volatile  dword  PACKETPTR;
+  volatile  dword  FREQUENCY;
+  volatile  dword  TXPOWER;
+  volatile  dword  MODE;
+  volatile  dword  PCNF0;
+  volatile  dword  PCNF1;
+  volatile  dword  BASE0;
+  volatile  dword  BASE1;
+  volatile  dword  PREFIX0;
+  volatile  dword  PREFIX1;
+  volatile  dword  TXADDRESS;
+  volatile  dword  RXADDRESSES;
+  volatile  dword  CRCCNF;
+  volatile  dword  CRCPOLY;
+  volatile  dword  CRCINIT;
+  volatile  dword  RESERVED9;
+  volatile  dword  TIFS;
+  volatile  dword  RSSISAMPLE;
+  volatile  dword  RESERVED10;
+  volatile  dword  STATE;
+  volatile  dword  DATAWHITEIV;
+  volatile  dword  RESERVED11[2];
+  volatile  dword  BCC;
+  volatile  dword  RESERVED12[39];
+  volatile  dword  DAB[8];
+  volatile  dword  DAP[8];
+  volatile  dword  DACNF;
+  volatile  dword  MHRMATCHCONF;
+  volatile  dword  MHRMATCHMAS;
+  volatile  dword  RESERVED13;
+  volatile  dword  MODECNF0;
+  volatile  dword  RESERVED14[3];
+  volatile  dword  SFD;
+  volatile  dword  EDCNT;
+  volatile  dword  EDSAMPLE;
+  volatile  dword  CCACTRL;
+  volatile  dword  RESERVED15[611];
+  volatile  dword  POWER;
+} *nrfRadioPtr;
+
+
+
+#define NrfRadioBase    0x40001000
+#define NrfRadioPtr     ((nrfRadioPtr) NrfRadioBase)
+
+#ifndef NrfPowerBase
+#define NrfPowerBase    0x40000000
+#define nrfPowerDCDCEN  ((dword *) 0x40000578)
+#endif
+
+#ifndef NrfClockBase
+#define NrfClockBase    0x40000000
+#define nrfClockTASKS_HFCLKSTART  ((dword *) 0x40000000)
+#endif
+
+// Direktverbindungen (shortcuts) zwischen events und Tasks
+//
+#define NrfScREADY_START    0x00000001
+#define NrfScEND_DISABLE    0x00000002
+#define NrfScDISABLED_TXEN  0x00000004
+#define NrfScDISABLED_RXEN  0x00000008
+#define NrfScTXREADY_START  0x00040000
+#define NrfScRXREADY_START  0x00080000
+
+// Interrupts
+//
+#define NrfIntREADY         0x00000001
+#define NrfIntADDRESS       0x00000002
+#define NrfIntPAYLOAD       0x00000004
+#define NrfIntEND           0x00000008
+#define NrfIntDISABLED      0x00000010
+#define NrfIntRSSIEND       0x00000080
+#define NrfIntTXREADY       0x00200000
+#define NrfIntRXREADY       0x00400000
+
+// Zustände
+//
+#define NrfStDISABLED       0
+#define NrfStRXRU           1
+#define NrfStRXIDLE         2
+#define NrfStRX             3
+#define NrfStRXDISABLE      4
+#define NrfStTXRU           9
+#define NrfStTXIDLE         10
+#define NrfStTX             11
+#define NrfStTXDISABLE      12
+
+// Festlegungen für die Paketkonfigurationsregister
+//
+
+#define PCNF0_LFLEN(x)    x
+// Anzahl der Bits im Längenfeld (0-15)
+
+#define PCNF0_S0LEN(x)    (x << 8)
+// Länge des Header0 (S0) in Bytes (0 oder 1)
+
+#define PCNF0_S1LEN(x)    (x << 16)
+// Länge des S1-Feldes in Bit (0 bis 15)
+
+#define PCNF1_MAXLEN(x)   x
+// Maximale Telegrammlänge (0 bis 255)
+
+#define PCNF1_BALEN(x)    (x << 16)
+// Basislänge der Zugriffsadresse (Access Address, 2-4)
+
+#define PCNF1_WHITEEN(x)  (x << 25)
+// Whitening (Bitmischung) Ein/Aus (1/0)
+
+// Festlegungen für die CRC-Generierung
+//
+
+#define CRCCNF_LEN(x)     x
+// Anzahl der Bytes für CRC (0-3)
+
+#define CRCCNF_SKIPADDR(x)  (x << 8)
+// Zugriffsadresse (Access Address) nicht im CRC (1), im CRC (0)
+
+
+typedef struct _nrf52840Cfg
+{
+  dword   pCnf0;
+  dword   pCnf1;
+  dword   whiteInit;
+  dword   modeCnf0;
+  dword   crcPoly;
+  dword   crcInit;
+  dword   crcCnf;
+  dword   packetPtr;
+  dword   frequency;
+  dword   txPower;
+  dword   mode;
+  dword   dacnf;
+  dword   rxAddrEn;
+  dword   base0;
+  dword   prefix0;
+  dword   txAddr;
+  dword   rxAddr;
+
+}nrf52840Cfg, *nrf52840CfgPtr;
+
+// ----------------------------------------------------------------------------
+
+class nRF52840Radio : IntrfRadio
+{
+private:
+  // --------------------------------------------------------------------------
+  // Lokale Daten
+  // --------------------------------------------------------------------------
+  //
+  byte        pduMem[256];
+  byte        pduSentE[256];
+  byte        pduSentS[256];
+
+  bcPduPtr    pmPtr;
+  bcPduPtr    pePtr;
+  bcPduPtr    psPtr;
+
+  nrf52840Cfg cfgData;
+
+  bool        recMode;
+  bool        eadM;
+  bool        nakM;
+  bool        comFin;
+  bool        comError;
+  bool        newValues;
+
+  dword       irqCounter;
+  TxMode      trfMode;
+
+  TxStatistics    statList[NrOfTxModes];
+  TxStatisticsPtr statisticPtr;
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen der Basis-Klasse
+  // --------------------------------------------------------------------------
+
+  nRF52840Radio();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  void  begin();
+  void  setAccessAddress(dword addr); // Setzen der Zugriffsadresse
+  void  setPacketParms(blePduType type);
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+  void  setChannel(int nr);           // Schalten physikalischer Kanal
+  int   sendSync(bcPduPtr inPduPtr, TxStatePtr refState);
+
+  void  send(bcPduPtr inPduPtr, TxMode txMode);
+  void  send(bcPduPtr inPduPtrE, bcPduPtr inPduPtrS, TxMode txMode, bool newValues);
+  int   getRecData(bcPduPtr data, TxMode txMode, int max);  // Empfangene Daten lesen
+
+  void  disable(TxMode txMode);
+  bool  disabled(TxMode txMode);      // Abfrage, ob ausgeschaltet
+  void  cont(TxMode txMode);
+  bool  fin(TxMode txMode, bool *err);
+                                      // Senden eines Telegramms (und warten)
+  int   startRec();                   // Datenempfang starten
+  int   contRec();                    // Datenempfang fortsetzen
+  int   endRec();                     // Datenempfang beenden
+  int   checkRec();                   // Zustand Datenempfang feststellen
+  int   getRecData(bcPduPtr data, int max);  // Empfangene Daten lesen
+
+  void  setPower(int DBm);            // Leistung des Senders in DBm
+
+  void  readCheckCfg();               // Konfigurationsdaten auslesen
+
+  static  nRF52840Radio *instPtr0;
+  static  void irqHandler0();
+
+  void    irqHandler();
+
+  // --------------------------------------------------------------------------
+  // Datenzugriffe
+  // --------------------------------------------------------------------------
+  //
+  int   getStatistics(TxStatisticsPtr dest);
+  int   getState();
+
+  // ----------------------------------------------------------------------------
+  //                      D e b u g - H i l f e n
+  // ----------------------------------------------------------------------------
+  //
+  int   getPduMem(byte *dest, int start, int end);
+  int   getPduSent(byte *dest, int start, int end);
+
+
+};
+
+
+#endif // NRF52840RADIO_H
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Ser/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Ser/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..fa5d17537ce11418ad11ee8b2689c1ae0ba6a20b
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Ser/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "nRF52840Ser",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Ser/nRF52840Ser.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Ser/nRF52840Ser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..86b66bb6cd5ba3f8e3a58c8587d7df52eac240f5
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Ser/nRF52840Ser.cpp
@@ -0,0 +1,323 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Ser.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#include "nRF52840Ser.h"
+#include <string.h>
+
+// ----------------------------------------------------------------------------
+// Initialisierungen
+// ----------------------------------------------------------------------------
+
+nRF52840Ser::nRF52840Ser()
+{
+  serPtr      = NULL;
+  instPtr0    = NULL;
+  irqCounter  = 0;
+  curIntEn    = 0;
+  curIRQ      = 0;
+  lastError   = 0;
+  cntError    = 0;
+}
+
+dword speedArr[18] =
+{
+    0x0004F000, 0x0009D000, 0x0013B000,
+    0x00275000, 0x003B0000, 0x004EA000,
+    0x0075F000, 0x00800000, 0x009D5000,
+    0x00E50000, 0x00EBF000, 0x013A9000,
+    0x01D7E000, 0x03AFB000, 0x04000000,
+    0x075F7000, 0x0EBED000, 0x10000000
+};
+
+// ----------------------------------------------------------------------------
+// Konfiguration
+// ----------------------------------------------------------------------------
+//
+void nRF52840Ser::begin(SerParamsPtr inParPtr, IntrfBuf *bufferIf)
+{
+  nrfGpioPtr  gpioPtr;
+  dword       regVal;
+
+  // Setzen des Peripheriezeigers anhand der Instanz
+  // und Initialisieren weiterer Variablen/Zeiger
+  //
+  serPtr = NrfSerPtr0;
+  clrAllEvents();
+  instPtr0  = this;
+  curIRQ    = 2;
+
+  // Interruptvektor setzen
+  //
+  __NVIC_SetVector((IRQn_Type) 2, (dword) nRF52840Ser::irqHandler0);
+  __NVIC_SetPriority((IRQn_Type) 2, 1);
+  __NVIC_EnableIRQ((IRQn_Type) 2);
+
+  // Alternative Peripherie (gleiche ID, also Alles) abschalten
+  //
+  serPtr->ENABLE = SerDisable;
+
+
+  // TxD
+  // -------------------------------------------------
+  //
+  // Pins zuweisen und initialisieren
+  //
+  if(inParPtr->txdPort == 1)
+  {
+    regVal = 32 + inParPtr->txdPin;
+    gpioPtr = NrfGpioPtr1;
+  }
+  else
+  {
+    regVal = inParPtr->txdPin;
+    gpioPtr = NrfGpioPtr0;
+  }
+
+
+  // Connect (hoechstwertiges Bit) beruecksichtigen
+  //
+  serPtr->PSEL_TXD = regVal | 0x7FFFFFC0;
+
+  // Zugewiesenen Pin als Ausgang schalten und Treibermodus setzen
+  // Laut Datenblatt ist das entsprechende Bit im Konfigurationsregister
+  // mit dem DIR-Register physikalisch verbunden
+  //
+  if(inParPtr->type == stStd)
+    regVal = GpioPinCnf_DRIVE(GpioDriveS0S1);
+  else if(inParPtr->type == stPow)
+    regVal = GpioPinCnf_DRIVE(GpioDriveH0H1);
+  else
+    regVal = GpioPinCnf_DRIVE(GpioDriveH0D1);
+
+  gpioPtr->PIN_CNF[inParPtr->txdPin] = regVal | GpioPinCnf_DIROUT;
+
+  // RXD
+  // -------------------------------------------------
+  //
+  if(inParPtr->rxdPort == 1)
+  {
+    regVal = 32 + inParPtr->rxdPin;
+    gpioPtr = NrfGpioPtr1;
+  }
+  else
+  {
+    regVal = inParPtr->rxdPin;
+    gpioPtr = NrfGpioPtr0;
+  }
+
+  // Connect (hoechstwertiges Bit) beruecksichtigen
+  //
+  serPtr->PSEL_RXD = regVal | 0x7FFFFFC0;
+
+  // Zugewiesenen Pin als Eingang schalten und Treibermodus setzen
+  // Laut Datenblatt ist das entsprechende Bit im Konfigurationsregister
+  // mit dem DIR-Register physikalisch verbunden
+  //
+  gpioPtr->PIN_CNF[inParPtr->rxdPin] = GpioPinCnf_PULL(GpioPullUp);
+
+  // Bitrate einstellen
+  //
+  regVal = speedArr[inParPtr->speed];
+  serPtr->BAUDRATE = regVal;
+  serPtr->SHORTS = 0;
+
+  // Interrupts freischalten
+  //
+  curIntEn = (SerInt_TXDRDY | SerInt_RXDRDY | SerInt_ERROR);
+  serPtr->INTENSET = curIntEn;
+
+  // Und bereit machen
+  //
+  serPtr->ENABLE = SerEnable;
+  txdFin = true;
+
+  bufIf = bufferIf;
+  delay(10);
+}
+
+
+// ----------------------------------------------------------------------------
+// Steuerfunktionen, gezielte Prozessorzugriffe und Hilfsfunktionen
+// ----------------------------------------------------------------------------
+//
+void nRF52840Ser::clrAllEvents()
+{
+  serPtr->EVENTS_RXDRDY     = 0;
+  serPtr->EVENTS_TXDRDY     = 0;
+  serPtr->EVENTS_ERROR      = 0;
+}
+
+// Fortsetzen des Interrupt-Sendebetriebs
+//
+void nRF52840Ser::resuSend()
+{
+  byte  td;
+
+  if(!txdFin) return;
+  if(bufIf == NULL) return;
+  if(!bufIf->getByteSnd(&td)) return;
+
+  txdFin = false;
+  serPtr->EVENTS_TXDRDY = 0;
+  serPtr->TXD = td;
+}
+
+// Starten des Sendebetriebs
+//
+void nRF52840Ser::startSend()
+{
+  serPtr->TASKS_STARTTX = 1;
+}
+
+// Anhalten des Sendebetriebs
+//
+void nRF52840Ser::stopSend()
+{
+  serPtr->TASKS_STOPTX = 1;
+}
+
+// Starten des Empfangsbetriebs
+//
+void nRF52840Ser::startRec()
+{
+  serPtr->TASKS_STARTRX = 1;
+}
+
+// Anhalten des Empfangsbetriebs
+//
+void nRF52840Ser::stopRec()
+{
+  serPtr->TASKS_STOPRX = 1;
+}
+
+
+// Bedingtes Ausgeben eines Zeichens
+//
+bool nRF52840Ser::condSend(byte c)
+{
+  if(!txdFin) return(false);
+
+  txdFin = false;
+  serPtr->EVENTS_TXDRDY = 0;
+  serPtr->TXD = c;
+  return(true);
+}
+
+// ----------------------------------------------------------------------------
+// Ereignisbearbeitung und Interrupts
+// ----------------------------------------------------------------------------
+//
+nRF52840Ser *nRF52840Ser::instPtr0 = NULL;
+
+void nRF52840Ser::irqHandler0()
+{
+  if(instPtr0 == NULL) return;
+  instPtr0->irqCounter++;
+  instPtr0->irqHandler();
+}
+
+
+  // --------------------------------------------------------------------------
+  // Interrupts (Ereignisbehandlung)
+  // --------------------------------------------------------------------------
+  //
+void nRF52840Ser::irqHandler()
+{
+  byte  b;
+
+  if(serPtr->EVENTS_TXDRDY != 0)
+  {
+    serPtr->EVENTS_TXDRDY = 0;
+    if(bufIf == NULL) return;
+
+    if(!bufIf->getByteSnd(&b))
+      txdFin = true;
+    else
+      serPtr->TXD = b;
+  }
+  else if(serPtr->EVENTS_RXDRDY != 0)
+  {
+    serPtr->EVENTS_RXDRDY = 0;
+    b = serPtr->RXD;
+    if(bufIf == NULL) return;
+    bufIf->putByteRec(b);
+  }
+  else if(serPtr->EVENTS_ERROR != 0)
+  {
+    serPtr->EVENTS_ERROR = 0;
+    cntError++;
+    lastError = serPtr->ERRORSRC;
+    anyError |= lastError;
+  }
+}
+
+// --------------------------------------------------------------------------
+// Datenzugriffe
+// --------------------------------------------------------------------------
+//
+// Letzten Fehler lesen (Bits)
+//
+int   nRF52840Ser::getLastError()
+{
+  return(lastError);
+}
+
+// Alle vorgekommenen Fehlerbits
+//
+int   nRF52840Ser::getAnyError()
+{
+  return(anyError);
+}
+
+// Anzahl der Fehler lesen
+//
+dword nRF52840Ser::getErrCount()
+{
+  return(cntError);
+}
+
+
+
+// ----------------------------------------------------------------------------
+//                      D e b u g - H i l f e n
+// ----------------------------------------------------------------------------
+//
+dword nRF52840Ser::getIrqCount()
+{
+  return(irqCounter);
+}
+
+void nRF52840Ser::resetIrqList()
+{
+  irqIdx = 0;
+  firstRead = true;
+
+  for(int i = 0; i < 8; i++)
+    irqList[i] = 0;
+}
+
+void nRF52840Ser::getIrqList(char *dest)
+{
+  int destIdx = 0;
+
+  for(int i = 0; i < 8; i++)
+  {
+    if(irqList[i] == 0) break;
+
+    dest[destIdx++] = ' ';
+    dest[destIdx++] = irqList[i] + 0x30;
+  }
+
+  dest[destIdx] = '\0';
+}
+
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Ser/nRF52840Ser.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Ser/nRF52840Ser.h
new file mode 100644
index 0000000000000000000000000000000000000000..37fcae4b19bc8ec15a2a83040f6f086d35e68efd
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Ser/nRF52840Ser.h
@@ -0,0 +1,234 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Ser.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#ifndef NRF52840SER_H
+#define NRF52840SER_H
+
+#include "Arduino.h"
+#include "arduinoDefs.h"
+#include "IntrfBuf.h"
+#include "IntrfSerial.h"
+
+// ----------------------------------------------------------------------------
+
+typedef struct _nrfSer
+{
+  volatile  dword  TASKS_STARTRX;           // 000
+  volatile  dword  TASKS_STOPRX;            // 004
+  volatile  dword  TASKS_STARTTX;           // 008
+  volatile  dword  TASKS_STOPTX;            // 00C
+  volatile  dword  Reserve01[3];            // 010
+  volatile  dword  TASKS_SUSPEND;           // 01C
+  volatile  dword  Reserve02[56];           // 020
+  volatile  dword  EVENTS_CTS;              // 100
+  volatile  dword  EVENTS_NCTS;             // 104
+  volatile  dword  EVENTS_RXDRDY;           // 108
+  volatile  dword  Reserve03[4];            // 10C
+  volatile  dword  EVENTS_TXDRDY;           // 11C
+  volatile  dword  Reserve04;               // 120
+  volatile  dword  EVENTS_ERROR;            // 124
+  volatile  dword  Reserve05[7];            // 128
+  volatile  dword  EVENTS_RXTO;             // 144
+  volatile  dword  Reserve06[46];           // 148
+  volatile  dword  SHORTS;                  // 200
+  volatile  dword  Reserve07[64];           // 204
+  volatile  dword  INTENSET;                // 304
+  volatile  dword  INTENCLR;                // 308
+  volatile  dword  Reserve08[93];           // 30C
+  volatile  dword  ERRORSRC;                // 480
+  volatile  dword  Reserve09[31];           // 484
+  volatile  dword  ENABLE;                  // 500
+  volatile  dword  Reserve10;               // 504
+  volatile  dword  PSEL_RTS;                // 508
+  volatile  dword  PSEL_TXD;                // 50C
+  volatile  dword  PSEL_CTS;                // 510
+  volatile  dword  PSEL_RXD;                // 514
+  volatile  dword  RXD;                     // 518
+  volatile  dword  TXD;                     // 51C
+  volatile  dword  Reserve11;               // 520
+  volatile  dword  BAUDRATE;                // 524
+  volatile  dword  Reserve12[17];           // 528
+  volatile  dword  CONFIG;                  // 56C
+} nrfSer, *nrfSerPtr;
+
+#define NrfSerBase0   0x40002000
+#define NrfSerPtr0    ((nrfSerPtr) NrfSerBase0)
+
+#ifndef nrfGpioDef
+
+typedef struct _nrfGpio
+{
+  volatile  dword Reserve01;                // 000
+  volatile  dword OUT;                      // 004
+  volatile  dword OUTSET;                   // 008
+  volatile  dword OUTCLR;                   // 00C
+  volatile  dword IN;                       // 010
+  volatile  dword DIR;                      // 014
+  volatile  dword DIRSET;                   // 018
+  volatile  dword DIRCLR;                   // 01C
+  volatile  dword LATCH;                    // 020
+  volatile  dword DETECTMODE;               // 024
+  volatile  dword Reserve02[118];           // 026
+  volatile  dword PIN_CNF[32];              // 200
+} nrfGpio, *nrfGpioPtr;
+
+#define NrfGpioBase   0x50000000
+#define NrfGpioBase0  0x50000500
+#define NrfGpioPtr0   ((nrfGpioPtr) NrfGpioBase0)
+#define NrfGpioBase1  0x50000800
+#define NrfGpioPtr1   ((nrfGpioPtr) NrfGpioBase1)
+
+#define GpioPinCnf_DIROUT     ((dword) 0x00000001)
+
+#define GpioPinCnf_DISBUF     ((dword) 0x00000002)
+
+#define GpioPinCnf_PULL(x)    ((dword) x << 2)
+#define GpioPullDown          1
+#define GpioPullUp            3
+
+#define GpioPinCnf_DRIVE(x)   ((dword) x << 8)
+#define GpioDriveS0S1         0
+#define GpioDriveH0S1         1
+#define GpioDriveS0H1         2
+#define GpioDriveH0H1         3
+#define GpioDriveD0S1         4
+#define GpioDriveD0H1         5
+#define GpioDriveS0D1         6
+#define GpioDriveH0D1         7
+
+#define GpioPinCnf_SENSE(x)   ((dword) x << 16)
+#define GpioSenseHigh         2
+#define GpioSenseLow          3
+
+#define nrfGpioDef
+#endif
+
+// Festlegungen für die Paketkonfigurationsregister
+//
+
+#define SerInt_RXDRDY     ((dword) 0x00000001 << 2)
+// Interrupt für Event RXDRDY
+
+#define SerInt_TXDRDY     ((dword) 0x00000001 << 7)
+// Interrupt für Event TXDRDY
+
+#define SerInt_ERROR      ((dword) 0x00000001 << 9)
+// Interrupt für Event ERROR
+
+
+#define SerEnable   4
+#define SerDisable  0
+
+// Bit-Masken fuer Kommunikationsbedingungen
+//
+#define BM_REC_NOT_COND 0x00
+// Keine Bedingungen beim Empfang
+#define BM_REC_END_CHR  0x01
+// Empfang Stoppen beim Eintreffen des vorgegebenen Zeichens
+#define BM_REC_RINGBUF  0x02
+// Receive characters in ring buffer
+#define BM_SND_RINGBUF  0x04
+// Transmit characters via ring buffer
+
+
+
+// ----------------------------------------------------------------------------
+
+class nRF52840Ser : IntrfSerial
+{
+private:
+  // --------------------------------------------------------------------------
+  // Lokale Daten und Funktionen
+  // --------------------------------------------------------------------------
+  //
+  nrfSerPtr     serPtr;
+  dword         irqCounter;
+
+  int           lastError;
+  int           anyError;
+  dword         cntError;
+
+  int           curIRQ;
+  dword         curIntEn;
+
+  IntrfBuf      *bufIf;
+  bool          txdFin;       // TRUE = Sendevorgang beendet
+
+  void clrAllEvents();
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen der Basis-Klasse
+  // --------------------------------------------------------------------------
+
+  nRF52840Ser();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  void begin(SerParamsPtr serParPtr, IntrfBuf *bufferIf);
+
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+  void resuSend();    // Fortsetzen des Interrupt-Sendebetriebs
+  void startSend();   // Starten des Sendebetriebs
+  void stopSend();    // Anhalten des Sendebetriebs
+
+  void startRec();    // Starten des Empfangsbetriebs
+  void stopRec();     // Anhalten des Empfangsbetriebs
+
+
+  // --------------------------------------------------------------------------
+  // Datenzugriffe
+  // --------------------------------------------------------------------------
+  //
+  bool condSend(byte c);  // Bedingtes Senden eines Zeichens
+
+  int   getLastError();   // Letzten Fehler lesen (Bits)
+  int   getAnyError();    // Alle vorgekommenen Fehlerbits
+  dword getErrCount();    // Anzahl der Fehler lesen
+
+
+  // ----------------------------------------------------------------------------
+  // Ereignisbearbeitung und Interrupts
+  // ----------------------------------------------------------------------------
+  //
+  static  nRF52840Ser *instPtr0;
+  static  void irqHandler0();
+
+  void    irqHandler();
+
+  // --------------------------------------------------------------------------
+  // lokale Variablen
+  // --------------------------------------------------------------------------
+  //
+
+
+  // ----------------------------------------------------------------------------
+  //                      D e b u g - H i l f e n
+  // ----------------------------------------------------------------------------
+  //
+  int           irqIdx;
+  int           irqList[8];
+
+  byte          extraValue;
+  bool          firstRead;
+
+  dword   getIrqCount();
+  void    resetIrqList();
+  void    getIrqList(char *dest);
+
+};
+
+#endif // NRF52840SER_H
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Twi/library.json b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Twi/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..16ae3922cdc02214ba2709422d1f9acfa86d132d
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Twi/library.json
@@ -0,0 +1,4 @@
+{
+  "name": "nRF52840Twi",
+  "version": "0.0.0+20220823165932"
+}
\ No newline at end of file
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Twi/nRF52840Twi.cpp b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Twi/nRF52840Twi.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..81fdb263721b902935a3a21f952b214796e8c9d5
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Twi/nRF52840Twi.cpp
@@ -0,0 +1,586 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Radio.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#include "nRF52840Twi.h"
+#include <string.h>
+
+// ----------------------------------------------------------------------------
+// Initialisierungen
+// ----------------------------------------------------------------------------
+
+nRF52840Twi::nRF52840Twi()
+{
+  twiPtr      = NULL;
+  instPtr0    = NULL;
+  instPtr1    = NULL;
+  irqCounter  = 0;
+}
+
+// ----------------------------------------------------------------------------
+// Konfiguration
+// ----------------------------------------------------------------------------
+//
+TwiError nRF52840Twi::begin(TwiParamsPtr inParPtr)
+{
+  TwiError    retv;
+  nrfGpioPtr  gpioPtr;
+  dword       regVal;
+
+  retv = TEnoError;
+
+  params = *inParPtr;
+
+  // Setzen des Peripheriezeigers anhand der Instanz
+  // und Initialisieren weiterer Variablen/Zeiger
+  //
+  if(inParPtr->inst == 0)
+  {
+    twiPtr    = NrfTwiPtr0;
+    clrAllEvents();
+    instPtr0  = this;
+    curIRQ    = 3;
+
+    // Interruptvektor setzen
+    //
+    __NVIC_SetVector((IRQn_Type) 3, (dword) nRF52840Twi::irqHandler0);
+    __NVIC_SetPriority((IRQn_Type) 3, 1);
+    __NVIC_EnableIRQ((IRQn_Type) 3);
+  }
+  else
+  {
+    twiPtr    = NrfTwiPtr1;
+    clrAllEvents();
+    instPtr1  = this;
+    curIRQ    = 4;
+
+    // Interruptvektor setzen
+    //
+    __NVIC_SetVector((IRQn_Type) 4, (dword) nRF52840Twi::irqHandler1);
+    __NVIC_SetPriority((IRQn_Type) 4, 1);
+    __NVIC_EnableIRQ((IRQn_Type) 4);
+  }
+
+  // Alternative Peripherie (gleiche ID, also Alles) abschalten
+  //
+  twiPtr->ENABLE = TwiDisable;
+
+
+  // Takt
+  // -------------------------------------------------
+  //
+  // Pins zuweisen und initialisieren
+  //
+  if(inParPtr->clkPort == 1)
+  {
+    regVal = 32 + inParPtr->clkPin;
+    gpioPtr = NrfGpioPtr1;
+  }
+  else
+  {
+    regVal = inParPtr->clkPin;
+    gpioPtr = NrfGpioPtr0;
+  }
+
+  // Connect (hoechstwertiges Bit) beruecksichtigen
+  //
+  twiPtr->PSEL_SCL = regVal | 0x7FFFFFC0;
+
+  // Zugewiesenen Pin als Eingang schalten und Treibermodus setzen
+  // Laut Datenblatt ist das entsprechende Bit im Konfigurationsregister
+  // mit dem DIR-Register physikalisch verbunden
+  //
+  gpioPtr->PIN_CNF[inParPtr->clkPin] = GpioPinCnf_DRIVE(GpioDriveS0D1);
+
+  // Daten
+  // -------------------------------------------------
+  //
+  if(inParPtr->dataPort == 1)
+  {
+    regVal = 32 + inParPtr->dataPin;
+    gpioPtr = NrfGpioPtr1;
+  }
+  else
+  {
+    regVal = inParPtr->dataPin;
+    gpioPtr = NrfGpioPtr0;
+  }
+
+  // Connect (hoechstwertiges Bit) beruecksichtigen
+  //
+  twiPtr->PSEL_SDA = regVal | 0x7FFFFFC0;
+
+  // Zugewiesenen Pin als Eingang schalten und Treibermodus setzen
+  // Laut Datenblatt ist das entsprechende Bit im Konfigurationsregister
+  // mit dem DIR-Register physikalisch verbunden
+  //
+  gpioPtr->PIN_CNF[inParPtr->dataPin] = GpioPinCnf_DRIVE(GpioDriveS0D1);
+
+  // Frequenz einstellen
+  //
+  if(inParPtr->speed == Twi100k)
+    regVal = NrfTwi100k;
+  else if(inParPtr->speed == Twi400k)
+    regVal = NrfTwi400k;
+  else
+    regVal = NrfTwi250k;
+
+  twiPtr->FREQUENCY = regVal;
+
+  twiPtr->SHORTS = 0;
+
+  // Interrupts freischalten
+  //
+  curIntEn = (TwiInt_TXDSENT | TwiInt_RXDREADY | TwiInt_ERROR | TwiInt_STOPPED);
+  twiPtr->INTENSET = curIntEn;
+
+  // Und bereit machen
+  //
+  twiPtr->ENABLE = TwiEnable;
+
+  delay(10);
+
+  return(retv);
+}
+
+
+void nRF52840Twi::getParams(TwiParamsPtr parPtr)
+{
+  *parPtr = params;
+}
+
+
+// ----------------------------------------------------------------------------
+// Steuerfunktionen, gezielte Prozessorzugriffe und Hilfsfunktionen
+// ----------------------------------------------------------------------------
+//
+void nRF52840Twi::clrAllEvents()
+{
+  twiPtr->EVENTS_STOPPED    = 0;
+  twiPtr->EVENTS_RXDREADY   = 0;
+  twiPtr->EVENTS_TXDSENT    = 0;
+  twiPtr->EVENTS_SUSPENDED  = 0;
+  twiPtr->EVENTS_BB         = 0;
+  twiPtr->EVENTS_ERROR      = 0;
+}
+
+// ----------------------------------------------------------------------------
+//                      M a s t e r
+// ----------------------------------------------------------------------------
+//
+
+  // --------------------------------------------------------------------------
+  // Datenaustausch
+  // --------------------------------------------------------------------------
+  //
+TwiError nRF52840Twi::sendByte(int adr, TwiBytePtr refByte)
+{
+  TwiError retv = TEnoError;
+  lastError     = 0;
+
+  resetIrqList();
+
+  byteStruPtr     = refByte;
+  twiPtr->ADDRESS = adr;
+
+  byteStruPtr->twiStatus  = TwStWrReq;
+  trfMode                 = ttmWriteByte;
+
+  twiPtr->TASKS_STARTTX   = 1;
+  twiPtr->TXD             = refByte->value;
+
+  return(retv);
+}
+
+TwiError nRF52840Twi::sendByteReg(int adr, int reg, TwiBytePtr refByte)
+{
+  TwiError retv = TEnoError;
+
+  //dynHand = &nRF52840Twi::irqHandler;
+
+  resetIrqList();
+
+  byteStruPtr     = refByte;
+  twiPtr->ADDRESS = adr;
+
+  byteStruPtr->twiStatus  = TwStWrReq;
+  trfMode                 = ttmWriteByteReg;
+  comIdx                  = 1;
+  irqIdx                  = 0;
+
+  twiPtr->TASKS_STARTTX   = 1;
+  twiPtr->TXD             = reg;
+
+  return(retv);
+}
+
+TwiStatus nRF52840Twi::writeByteReg(int adr, int reg, byte value)
+{
+  twiPtr->INTENCLR = curIntEn;
+
+  tmpByte.value = value;
+
+  sendByteReg(adr, reg, &tmpByte);
+
+  while(tmpByte.twiStatus != TwStFin)
+  {
+    irqHandler();
+
+    if(tmpByte.twiStatus & TwStError)
+      break;
+  }
+
+  twiPtr->INTENSET = curIntEn;
+
+  return(tmpByte.twiStatus);
+}
+
+
+TwiError nRF52840Twi::recByteReg(int adr, int reg, TwiBytePtr refByte)
+{
+  TwiError retv = TEnoError;
+
+  //dynHand = &nRF52840Twi::irqHandler;
+
+  byteStruPtr     = refByte;
+  twiPtr->ADDRESS = adr;
+
+  resetIrqList();
+
+  byteStruPtr->twiStatus  = TwStRdReq;
+  trfMode                 = ttmReadByteReg;
+
+  twiPtr->TASKS_STARTTX   = 1;
+  twiPtr->TXD             = reg;
+
+  return(retv);
+}
+
+int nRF52840Twi::readByteReg(int adr, int reg)
+{
+  twiPtr->INTENCLR = curIntEn;
+
+  recByteReg(adr, reg, &tmpByte);
+
+  while(tmpByte.twiStatus != TwStFin)
+  {
+    irqHandler();
+
+    if(tmpByte.twiStatus & TwStError)
+      break;
+  }
+
+  twiPtr->INTENSET = curIntEn;
+
+  if(tmpByte.twiStatus == TwStFin)
+    return(tmpByte.value);
+  else
+    return(-1);
+}
+
+
+TwiError nRF52840Twi::recByteRegSeq(int adr, int reg, TwiByteSeqPtr refByteSeq)
+{
+  TwiError retv = TEnoError;
+
+  byteSeqPtr      = refByteSeq;
+  comIdx          = 0;
+  twiPtr->ADDRESS = adr;
+
+  byteSeqPtr->twiStatus   = TwStRdReq;
+  trfMode                 = ttmReadByteRegSeq;
+
+  twiPtr->TASKS_STARTTX   = 1;
+  twiPtr->TXD             = reg;
+
+  return(retv);
+}
+
+TwiStatus nRF52840Twi::readByteRegSeq(int adr, int reg, TwiByteSeqPtr refByteSeq)
+{
+  byteSeqPtr      = refByteSeq;
+  comIdx          = 0;
+  twiPtr->ADDRESS = adr;
+
+  byteSeqPtr->twiStatus   = TwStRdReq;
+  trfMode                 = ttmReadByteRegSeq;
+
+  twiPtr->INTENCLR        = curIntEn;
+
+  twiPtr->TASKS_STARTTX   = 1;
+  twiPtr->TXD             = reg;
+
+  while(byteSeqPtr->twiStatus != TwStFin)
+  {
+    irqHandler();
+
+    if(byteSeqPtr->twiStatus & TwStError)
+      break;
+  }
+
+  twiPtr->INTENSET        = curIntEn;
+
+  return(byteSeqPtr->twiStatus);
+}
+
+// ----------------------------------------------------------------------------
+// Ereignisbearbeitung und Interrupts
+// ----------------------------------------------------------------------------
+//
+nRF52840Twi *nRF52840Twi::instPtr0 = NULL;
+
+void nRF52840Twi::irqHandler0()
+{
+  if(instPtr0 == NULL) return;
+  instPtr0->irqCounter++;
+  instPtr0->irqHandler();
+}
+
+nRF52840Twi *nRF52840Twi::instPtr1 = NULL;
+
+void nRF52840Twi::irqHandler1()
+{
+  if(instPtr1 == NULL) return;
+  instPtr1->irqCounter++;
+  instPtr1->irqHandler();
+}
+
+
+  // --------------------------------------------------------------------------
+  // Interrupts (Ereignisbehandlung)
+  // --------------------------------------------------------------------------
+  //
+void nRF52840Twi::irqHandler()
+{
+  switch(trfMode)
+  {
+    case ttmWriteByte:
+    // ------------------------------------------------------------------------
+
+      if(twiPtr->EVENTS_ERROR)
+      {
+        twiPtr->EVENTS_ERROR    = 0;
+        lastError               = twiPtr->ERRORSRC;
+        twiPtr->ERRORSRC        = (lastError & 0x06);   // Clear AdrNak/DataNak
+        byteStruPtr->twiStatus  = (TwiStatus) ( (int) TwStError + lastError);
+        twiPtr->TASKS_STOP      = 1;
+
+        irqList[irqIdx++] = 8;
+        return;
+      }
+
+      if(twiPtr->EVENTS_TXDSENT)
+      {
+        twiPtr->EVENTS_TXDSENT   = 0;
+        byteStruPtr->twiStatus   = TwStSent;
+        twiPtr->TASKS_STOP       = 1;
+
+        irqList[irqIdx++] = 1;
+        return;
+      }
+
+      if(twiPtr->EVENTS_STOPPED)
+      {
+        twiPtr->EVENTS_STOPPED  = 0;
+        if(lastError == 0)
+          byteStruPtr->twiStatus  = TwStFin;
+
+        irqList[irqIdx++] = 3;
+        return;
+      }
+
+      break;
+
+    case ttmWriteByteReg:
+    // ------------------------------------------------------------------------
+
+      if(twiPtr->EVENTS_ERROR)
+      {
+        twiPtr->EVENTS_ERROR    = 0;
+        lastError               = twiPtr->ERRORSRC;
+        twiPtr->ERRORSRC        = (lastError & 0x06);   // Clear AdrNak/DataNak
+        byteStruPtr->twiStatus  = (TwiStatus) ( (int) TwStError + lastError);
+        twiPtr->TASKS_STOP      = 1;
+        return;
+      }
+
+      if(twiPtr->EVENTS_TXDSENT)
+      {
+        twiPtr->EVENTS_TXDSENT   = 0;
+        byteStruPtr->twiStatus   = TwStSent;
+        if(comIdx == 1)
+        {
+          comIdx = 0;
+          twiPtr->TXD = byteStruPtr->value;
+        }
+        else
+          twiPtr->TASKS_STOP    = 1;
+        return;
+      }
+
+      if(twiPtr->EVENTS_STOPPED)
+      {
+        twiPtr->EVENTS_STOPPED  = 0;
+        if(lastError == 0)
+          byteStruPtr->twiStatus  = TwStFin;
+        return;
+      }
+
+      break;
+
+    case ttmReadByteReg:
+    // ------------------------------------------------------------------------
+
+      if(twiPtr->EVENTS_ERROR)
+      {
+        twiPtr->EVENTS_ERROR    = 0;
+        lastError               = twiPtr->ERRORSRC;
+        twiPtr->ERRORSRC        = (lastError & 0x06);   // Clear AdrNak/DataNak
+        byteStruPtr->twiStatus  = (TwiStatus) ( (int) TwStError + lastError);
+        twiPtr->TASKS_STOP      = 1;
+
+        irqList[irqIdx++] = 8;
+        return;
+      }
+
+      if(twiPtr->EVENTS_TXDSENT)
+      {
+        twiPtr->EVENTS_TXDSENT  = 0;
+        byteStruPtr->twiStatus  = TwStSent;
+        twiPtr->TASKS_STARTRX   = 1;
+        twiPtr->SHORTS          = 2;
+
+        irqList[irqIdx++] = 1;
+        return;
+      }
+
+      if(twiPtr->EVENTS_STOPPED)
+      {
+        twiPtr->EVENTS_STOPPED  = 0;
+        if(lastError == 0)
+          byteStruPtr->twiStatus  = TwStFin;
+
+        irqList[irqIdx++] = 3;
+        twiPtr->SHORTS = 0;
+        return;
+      }
+
+      if(twiPtr->EVENTS_RXDREADY)
+      {
+        twiPtr->EVENTS_RXDREADY = 0;
+        byteStruPtr->twiStatus  = TwStRecvd;
+        byteStruPtr->value      = twiPtr->RXD;
+
+        irqList[irqIdx++] = 2;
+        return;
+      }
+
+      break;
+
+    case ttmReadByteRegSeq:
+    // ------------------------------------------------------------------------
+
+      if(twiPtr->EVENTS_ERROR)
+      {
+        twiPtr->EVENTS_ERROR    = 0;
+        lastError               = twiPtr->ERRORSRC;
+        twiPtr->ERRORSRC        = (lastError & 0x06);   // Clear AdrNak/DataNak
+        byteSeqPtr->twiStatus   = (TwiStatus) ( (int) TwStError + lastError);
+        twiPtr->TASKS_STOP      = 1;
+        return;
+      }
+
+      if(twiPtr->EVENTS_TXDSENT)
+      {
+        twiPtr->EVENTS_TXDSENT  = 0;
+        byteSeqPtr->twiStatus   = TwStSent;
+        twiPtr->TASKS_STARTRX   = 1;
+        return;
+      }
+
+      if(twiPtr->EVENTS_STOPPED)
+      {
+        twiPtr->EVENTS_STOPPED   = 0;
+        if(lastError == 0)
+          byteSeqPtr->twiStatus  = TwStFin;
+        twiPtr->SHORTS = 0;
+        return;
+      }
+
+      if(twiPtr->EVENTS_RXDREADY)
+      {
+        twiPtr->EVENTS_RXDREADY       = 0;
+        byteSeqPtr->twiStatus         = TwStRecvd;
+        /*
+        if(comIdx == (byteSeqPtr->len - 2))
+          twiPtr->SHORTS              = 2;
+        byteSeqPtr->valueRef[comIdx]  = twiPtr->RXD;
+        if(comIdx < (byteSeqPtr->len - 1))
+          comIdx++;
+        */
+        if(comIdx == (byteSeqPtr->len - 2))
+          twiPtr->SHORTS              = 2;
+
+        lastIn = twiPtr->RXD;
+
+        if(comIdx < (byteSeqPtr->len))
+          byteSeqPtr->valueRef[comIdx]  = lastIn;
+
+        comIdx++;
+        return;
+      }
+
+      break;
+
+  }
+}
+
+// ----------------------------------------------------------------------------
+//                      S l a v e
+// ----------------------------------------------------------------------------
+
+// Starten des Datenempfangs
+//
+
+// ----------------------------------------------------------------------------
+//                      D e b u g - H i l f e n
+// ----------------------------------------------------------------------------
+//
+dword nRF52840Twi::getIrqCount()
+{
+  return(irqCounter);
+}
+
+void nRF52840Twi::resetIrqList()
+{
+  irqIdx = 0;
+  firstRead = true;
+
+  for(int i = 0; i < 8; i++)
+    irqList[i] = 0;
+}
+
+void nRF52840Twi::getIrqList(char *dest)
+{
+  int destIdx = 0;
+
+  for(int i = 0; i < 8; i++)
+  {
+    if(irqList[i] == 0) break;
+
+    dest[destIdx++] = ' ';
+    dest[destIdx++] = irqList[i] + 0x30;
+  }
+
+  dest[destIdx] = '\0';
+}
+
+
+
+
+
diff --git a/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Twi/nRF52840Twi.h b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Twi/nRF52840Twi.h
new file mode 100644
index 0000000000000000000000000000000000000000..e33ca74b72c254311a695ab5ea7fd3152a8ebf4d
--- /dev/null
+++ b/sketches/_PIO_Sketches/Karger/SoaapBleSlave_Test_2/src/nRF52840Twi/nRF52840Twi.h
@@ -0,0 +1,250 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   nRF52840Twi.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (wikipedia: Creative Commons)
+//
+
+#ifndef NRF52840TWI_H
+#define NRF52840TWI_H
+
+#include "Arduino.h"
+#include "arduinoDefs.h"
+#include "IntrfTw.h"
+
+// ----------------------------------------------------------------------------
+
+typedef struct _nrfTwi
+{
+  volatile  dword  TASKS_STARTRX;           // 000
+  volatile  dword  Reserve01;               // 004
+  volatile  dword  TASKS_STARTTX;           // 008
+  volatile  dword  Reserve02[2];            // 00C
+  volatile  dword  TASKS_STOP;              // 014
+  volatile  dword  Reserve03;               // 018
+  volatile  dword  TASKS_SUSPEND;           // 01C
+  volatile  dword  TASKS_RESUME;            // 020
+  volatile  dword  Reserve04[56];           // 024
+  volatile  dword  EVENTS_STOPPED;          // 104
+  volatile  dword  EVENTS_RXDREADY;         // 108
+  volatile  dword  Reserve05[4];            // 118
+  volatile  dword  EVENTS_TXDSENT;          // 11C
+  volatile  dword  Reserve06;               // 120
+  volatile  dword  EVENTS_ERROR;            // 124
+  volatile  dword  Reserve07[4];            // 128
+  volatile  dword  EVENTS_BB;               // 138
+  volatile  dword  Reserve08[3];            // 13C
+  volatile  dword  EVENTS_SUSPENDED;        // 148
+  volatile  dword  Reserve09[45];           // 14C
+  volatile  dword  SHORTS;                  // 200
+  volatile  dword  Reserve10[64];           // 204
+  volatile  dword  INTENSET;                // 304
+  volatile  dword  INTENCLR;                // 308
+  volatile  dword  Reserve11[110];          // 30C
+  volatile  dword  ERRORSRC;                // 4C4
+  volatile  dword  Reserve12[14];           // 4C8
+  volatile  dword  ENABLE;                  // 500
+  volatile  dword  Reserve13;               // 504
+  volatile  dword  PSEL_SCL;                // 508
+  volatile  dword  PSEL_SDA;                // 50C
+  volatile  dword  Reserve14[2];            // 510
+  volatile  dword  RXD;                     // 518
+  volatile  dword  TXD;                     // 51C
+  volatile  dword  Reserve15;               // 520
+  volatile  dword  FREQUENCY;               // 524
+  volatile  dword  Reserve16[24];           // 528
+  volatile  dword  ADDRESS;                 // 588
+} nrfTwi, *nrfTwiPtr;
+
+#define NrfTwiBase0   0x40003000
+#define NrfTwiPtr0    ((nrfTwiPtr) NrfTwiBase0)
+#define NrfTwiBase1   0x40004000
+#define NrfTwiPtr1    ((nrfTwiPtr) NrfTwiBase1)
+
+#define NrfTwi100k    0x01980000
+#define NrfTwi250k    0x04000000
+#define NrfTwi400k    0x06680000
+
+typedef enum _TwiTrfMode
+{
+  ttmWriteByte = 1,
+  ttmWriteByteReg,
+  ttmReadByteReg,
+  ttmReadByteRegSeq
+} TwiTrfMode;
+
+#ifndef nrfGpioDef
+
+typedef struct _nrfGpio
+{
+  volatile  dword Reserve01;                // 000
+  volatile  dword OUT;                      // 004
+  volatile  dword OUTSET;                   // 008
+  volatile  dword OUTCLR;                   // 00C
+  volatile  dword IN;                       // 010
+  volatile  dword DIR;                      // 014
+  volatile  dword DIRSET;                   // 018
+  volatile  dword DIRCLR;                   // 01C
+  volatile  dword LATCH;                    // 020
+  volatile  dword DETECTMODE;               // 024
+  volatile  dword Reserve02[118];           // 026
+  volatile  dword PIN_CNF[32];              // 200
+} nrfGpio, *nrfGpioPtr;
+
+#define NrfGpioBase   0x50000000
+#define NrfGpioBase0  0x50000500
+#define NrfGpioPtr0   ((nrfGpioPtr) NrfGpioBase0)
+#define NrfGpioBase1  0x50000800
+#define NrfGpioPtr1   ((nrfGpioPtr) NrfGpioBase1)
+
+#define GpioPinCnf_DIR        ((dword) 0x00000001)
+
+#define GpioPinCnf_INPUT      ((dword) 0x00000001 << 1)
+
+#define GpioPinCnf_PULL(x)    ((dword) x << 2)
+#define GpioPullDown          1
+#define GpioPullUp            3
+
+#define GpioPinCnf_DRIVE(x)   ((dword) x << 8)
+#define GpioDriveS0S1         0
+#define GpioDriveH0S1         1
+#define GpioDriveS0H1         2
+#define GpioDriveH0H1         3
+#define GpioDriveD0S1         4
+#define GpioDriveD0H1         5
+#define GpioDriveS0D1         6
+#define GpioDriveH0D1         7
+
+#define GpioPinCnf_SENSE(x)   ((dword) x << 16)
+#define GpioSenseHigh         2
+#define GpioSenseLow          3
+
+#define nrfGpioDef
+#endif
+
+// Festlegungen für die Paketkonfigurationsregister
+//
+
+#define TwiInt_STOPPED    ((dword) 0x00000001 << 1)
+// Interrupt für Event STOPPED
+
+#define TwiInt_RXDREADY   ((dword) 0x00000001 << 2)
+// Interrupt für Event RXDREADY
+
+#define TwiInt_TXDSENT    ((dword) 0x00000001 << 7)
+// Interrupt für Event TXDSENT
+
+#define TwiInt_ERROR      ((dword) 0x00000001 << 9)
+// Interrupt für Event ERROR
+
+#define TwiInt_BB         ((dword) 0x00000001 << 14)
+// Interrupt für Event BB
+
+#define TwiInt_SUSPENDED  ((dword) 0x00000001 << 18)
+// Interrupt für Event SUSPENDED
+
+#define TwiEnable   5
+#define TwiDisable  0
+
+
+
+
+// ----------------------------------------------------------------------------
+
+class nRF52840Twi : IntrfTw
+{
+private:
+  // --------------------------------------------------------------------------
+  // Lokale Daten und Funktionen
+  // --------------------------------------------------------------------------
+  //
+  nrfTwiPtr     twiPtr;
+  dword         irqCounter;
+
+  TwiBytePtr    byteStruPtr;
+  TwiWordPtr    wordStruPtr;
+  TwiByteSeqPtr byteSeqPtr;
+
+  TwiTrfMode    trfMode;
+  dword         lastError;
+  byte          lastIn;
+  int           comIdx;
+
+  int           curIRQ;
+  dword         curIntEn;
+
+  TwiByte       tmpByte;
+
+  TwiParams     params;
+
+  void clrAllEvents();
+
+public:
+  // --------------------------------------------------------------------------
+  // Initialisierungen der Basis-Klasse
+  // --------------------------------------------------------------------------
+
+  nRF52840Twi();
+
+  // --------------------------------------------------------------------------
+  // Konfigurationen
+  // --------------------------------------------------------------------------
+  //
+  TwiError begin(TwiParamsPtr inParPtr);
+  void getParams(TwiParamsPtr parPtr);
+
+
+  // --------------------------------------------------------------------------
+  // Steuerfunktionen
+  // --------------------------------------------------------------------------
+  //
+
+  // --------------------------------------------------------------------------
+  // Datenaustausch
+  // --------------------------------------------------------------------------
+  //
+  // asynchrone Kommunikation, Zustand in TwiByte.twiStatus
+  //
+  TwiError sendByte(int adr, TwiBytePtr refByte);
+  TwiError sendByteReg(int addr, int reg, TwiBytePtr refByte);
+  TwiError recByteReg(int addr, int reg, TwiBytePtr refByte);
+  TwiError recByteRegSeq(int adr, int reg, TwiByteSeqPtr refByteSeq);
+
+  // synchrone Kommunikation
+  //
+  TwiStatus writeByteReg(int adr, int reg, byte value);
+  int       readByteReg(int adr, int reg);
+  TwiStatus readByteRegSeq(int adr, int reg, TwiByteSeqPtr refByteSeq);
+
+  // ----------------------------------------------------------------------------
+  // Ereignisbearbeitung und Interrupts
+  // ----------------------------------------------------------------------------
+  //
+  static  nRF52840Twi *instPtr0;
+  static  void irqHandler0();
+
+  static  nRF52840Twi *instPtr1;
+  static  void irqHandler1();
+
+  void    irqHandler();
+
+  // ----------------------------------------------------------------------------
+  //                      D e b u g - H i l f e n
+  // ----------------------------------------------------------------------------
+  //
+  int           irqIdx;
+  int           irqList[8];
+
+  byte          extraValue;
+  bool          firstRead;
+
+  dword   getIrqCount();
+  void    resetIrqList();
+  void    getIrqList(char *dest);
+
+};
+
+#endif // NRF52840RADIO_H
+