Skip to content
Snippets Groups Projects
Commit 3a4e0c04 authored by RobertPatzke's avatar RobertPatzke
Browse files

Library BlePoll added

parent bb4474ee
Branches
No related tags found
No related merge requests found
//-----------------------------------------------------------------------------
// 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;
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 = 10;
pollList[i].prioCnt = 0;
pollList[i].slIdx = 0;
pollList[i].status = 0;
}
}
// ----------------------------------------------------------------------------
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)
{
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 = plptMeas6;
plMode = plmSoaapM;
fullCycle = true;
}
else
{
valuePdu.appId = plptMeas6;
plMode = plmSoaapS;
//plMode = plmSoaapM;
}
}
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;
}
// --------------------------------------------------------------------------
// 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)
{
bool retv = false;
pduPtr->len = 28;
pduPtr->data[0]++; // Pdu-Counter
pduPtr->data[1] = valuePdu.type;
pduPtr->data[2] = valuePdu.appId;
newValue = cbData(plptMeas6, &pduPtr->data[4]);
if(newValue)
{
retv = true;
pduPtr->data[3]++; // measCnt
}
return(retv);
}
// --------------------------------------------------------------------------
// 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);
}
// 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
// ----------------------------------------------------------------------------
//
void BlePoll::smStartCom()
{
bleState = 2000;
if(pollStop) // Der Start der Datenübertragung kann
{ // verzögert werden
pollStopped = true;
return;
}
pduOut.len = 6;
radio->setChannel(chn);
if(master)
{
for(int i = 1; i <= pollMaxNr; i++)
{
int slIdx = pollList[i].slIdx;
pollList[i].prioCnt = slaveList[slIdx].prioSet;
}
nak = false;
pollIdx = 1;
next(smReqComS);
}
else
{
nak = true;
next(smWaitEadr);
}
}
// ----------------------------------------------------------------------------
// Datenübertragung Master S l a v e - > M a s t e r
// ----------------------------------------------------------------------------
//
void BlePoll::smReqComS()
{
bleState = 2100;
if(pollStop) // Das Polling kann
{ // angehalten werden
pollStopped = true;
return;
}
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;
nak = false;
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);
}
int tmpInt1, tmpInt2, tmpInt3;
void BlePoll::smWaitAckComS()
{
byte tmpByte;
short tmpShort;
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
//
curSlave->result.counter = pduIn.data[0];
curSlave->result.type = pduIn.data[1];
curSlave->result.appId = pduIn.data[2];
curSlave->result.measCnt = pduIn.data[3];
curSlave->result.meas[0] = *(word *) &pduIn.data[4];
curSlave->result.meas[1] = *(word *) &pduIn.data[6];
curSlave->result.meas[2] = *(word *) &pduIn.data[8];
curSlave->result.meas[3] = *(word *) &pduIn.data[10];
curSlave->result.meas[4] = *(word *) &pduIn.data[12];
curSlave->result.meas[5] = *(word *) &pduIn.data[14];
if(curSlave->delayCnt == 0)
{
tmpByte = curSlave->result.counter - curSlave->oldPduCount;
if(tmpByte > 1)
curSlave->cntLostPdu += tmpByte - 1;
tmpByte = curSlave->result.measCnt - curSlave->oldMeasCount;
if(tmpByte > 1)
curSlave->cntLostMeas += tmpByte - 1;
}
else curSlave->delayCnt--;
curSlave->oldPduCount = curSlave->result.counter;
curSlave->oldMeasCount = curSlave->result.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);
}
// ----------------------------------------------------------------------------
// Datenübertragung Slave M a s t e r < - > S l a v e
// ----------------------------------------------------------------------------
//
dword smStartComESCnt;
void BlePoll::smStartComES()
{
bool newValues;
byte lenValues;
bleState = 1310;
smStartComESCnt++;
if(cbData == NULL)
{
next(smIdle);
return;
}
if(!radio->disabled(txmResp))
{
radio->disable(txmResp);
cntWaitDisabled++;
return;
}
nak = true;
eadr = true;
setPduAddress(&pduIn);
pduIn.len = 6;
nak = false;
eadr = false;
setPduAddress(&pduOut);
newValues = getValues(&pduOut);
radio->setChannel(chn);
radio->send(&pduIn, &pduOut, txmResp, newValues);
setTimeOut(wdTimeOut);
next(smWaitComES);
}
void BlePoll::smWaitComES()
{
bleState = 1320;
radio->getStatistics(&statistic);
if(timeOut())
{
next(smStartComES);
return;
}
if(!radio->fin(txmResp, &crcError)) return;
next(smStartComES);
}
// --------------------------------------------------------------------------
// 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]);
}
//-----------------------------------------------------------------------------
// 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
//
typedef struct _PlPduBase
{
byte counter; // zyklischer Telegrammmzähler
byte type; // Kennzeichnung der Datenstruktur (AppType)
byte plData[29]; // weitere spezifische Nutzdaten
} PlPduBase, *PlPduBasePtr;
// Erweiterte Datenstruktur für die Nutzdaten
//
typedef struct _PlPduExtd
{
byte counter; // zyklischer Telegrammmzähler
byte type; // Kennzeichnung der Datenstruktur (AppType)
byte plData[247]; // weitere spezifische Nutzdaten
} PlPduExtd, *PlPduExtdPtr;
// Datentypen (type in plPduBase)
//
typedef enum _PlpType
{
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)
} PlpType, *PlpTypePtr;
// Spezifische Datenstrukturen
//
typedef struct _PlpFullMeas
{
byte counter; // zyklischer Telegrammmzähler
byte type; // Kennzeichnung der Datenstruktur (AppType)
word meas[12]; // Liste von 12 Messwerten
byte appId; // Kennzeichnung für Dateninhalte (PlpType)
byte align; // Wird nicht gesendet, kennzeichnet Alignement
} PlpFullMeas, *PlpFullMeasPtr;
typedef struct _PlpMeas3
{
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
{
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
{
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;
// Identifikator für die Art der Daten
//
typedef enum _MeasId
{
app // Gestaltung/Bedeutung der Daten aus Anwendung
} MeasId, *MeasIdPtr;
typedef struct _Slave
{
dword timeOut;
dword cntTo;
dword cntErrCrc;
dword cntNakEP;
dword cntAckDP;
dword cntLostPdu;
dword cntLostMeas;
dword delayCnt;
byte adr;
byte area;
byte chn;
byte pIdx;
word prioSet;
word minPrio;
PlpMeas6 result;
bool newPdu;
byte oldPduCount;
byte oldMeasCount;
} 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);
#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
atDHA // Dezentrale Hausautomatisierung
} AppType;
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;
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;
TxStatistics statistic;
PlpMeas6 valuePdu;
bool newValue;
// 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);
// 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);
// --------------------------------------------------------------------------
// 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();
// Test
//
// Leeres Polling
//
void stopEP();
void resumeEP();
bool stoppedEP();
// Laufender Betrieb
//
void start(PlMode inPlMode);
// --------------------------------------------------------------------------
// Zugriff auf Polling-Informationen
// --------------------------------------------------------------------------
//
int getSlaveList(byte *dest, int maxByte);
void resetPollCounters();
// --------------------------------------------------------------------------
// Debugging
// --------------------------------------------------------------------------
//
dword debGetDword(int idx);
dword getStatistics(TxStatisticsPtr dest);
SlavePtr getSlavePtr(int idx);
PollStatePtr getPollPtr(int idx);
};
// ----------------------------------------------------------------------------
#endif // BlePoll_h
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment