From f6849ea5348b32f89bd7218d4e2068fa6237c1d1 Mon Sep 17 00:00:00 2001 From: Dirk Jahnke Date: Sun, 18 Nov 2018 15:51:03 +0100 Subject: [PATCH] Implemented most of the messages. --- README.md | 37 +++++++++++++ src/clockMsg.h | 18 ------- src/config.h | 1 + src/display.h | 2 +- src/main.cpp | 2 +- src/radio.cpp | 139 +++++++++++++++++++++++++++++++++++++------------ src/radio.h | 62 +++++++++++++++++++++- 7 files changed, 208 insertions(+), 53 deletions(-) delete mode 100644 src/clockMsg.h diff --git a/README.md b/README.md index c8ab696..f5ba2e8 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,43 @@ The tasks of the master clock / controller is: * inform client clocks about the current time to be displayed * support multiple clock systems, as we have multiple arrangements of model railroads at the same location having their own times +# Principals + +* all messages are sent as broadcast messages without acknowledge on device level +* all devices listen to all messages and filter out those messages addressed to them +* all messages begin with a message type, to-address, from-address +* addresses are a number in the range of 0..255 + * address 0 is reserved for master/controller + * address 255 is reserved for a broadcast to all devices +* if acknowledges are necessary/wanted, then separate messages needs to be applied +* after receiving a message that needs an acknowledge, no device is allowed to send a message for a TBD. period of time except the requested device, which may send an acknowledge +* after sending a message, the same device must wait a TBD. period of time before sending another message + +# Protocol + +## Message structure + + +Type | To | From | Message +-- | -- | -- | -- +P,p,A,U,a,C,T,H,E | 0..255 | 0..255 | msg type dependent + +## Message definitions + +Nachricht | msgType | Attribute | Status +--------- | ------- | --------- | ------ +PairingOffering | P | FastclockName (8), ClockChannel | Implemented +PairingRequest | p | ClientName(10) | Implemented +PairingAck | A | FastclockName (8), ClientName (10), Network-Address | Implemented +UnpairRequest | U | tbd. | --- +UnpairAck | a | tbd. | --- +FastClock | C | FastclockName (8), Hour, Minute, Second, ClockSpeed, Weekday | Implemented +TextMessage | T | FastclockName (8), Message | Implemented +Hello | H | tbd. | --- +HelloEcho | E | RSSI, canRouteToMaster, FastclockName(8) | --- + + + ## Links / References - Digitrax Loconet PE: http://www.digitrax.com/static/apps/cms/media/documents/loconet/loconetpersonaledition.pdf diff --git a/src/clockMsg.h b/src/clockMsg.h deleted file mode 100644 index 82dddb8..0000000 --- a/src/clockMsg.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef clockMsg_h_included -#define clockMsg_h_included - -#include - -struct clockMsg_s { - uint8_t msgType; - uint8_t day; - uint8_t hour; - uint8_t minute; - uint8_t second; - uint8_t speed; // msPerModelSecond / 4 --> 0..250 -}; -#define msgType_Clock 'c' /* clock update, sent by master using broadcast */ -#define msgType_ReqReg 'R' /* Request Registration, sent by Master using broadcast; ask clients to register */ -#define msgType_Reg 'r' /* Registration message, sent by Client using 1:1 message to master */ - -#endif diff --git a/src/config.h b/src/config.h index 720d671..4fca769 100644 --- a/src/config.h +++ b/src/config.h @@ -21,5 +21,6 @@ // field sizes #define MAX_CLOCK_NAME_LEN 8 #define MAX_CLIENT_NAME_LEN 10 +#define MAX_TEXT_MESSAGE_LEN 10 #endif diff --git a/src/display.h b/src/display.h index 613cdbf..1cc9eb9 100644 --- a/src/display.h +++ b/src/display.h @@ -28,7 +28,7 @@ public: clockHour = hour; clockMinute = minute; Serial.print("display: new time "); Serial.print(clockHour); Serial.print(":"); Serial.println(clockMinute); - } + }; //void setClockSpeed(int _msPerModelSecond) { msPerModelSecond = _msPerModelSecond; setClockSpeed("x"); }; void setClockWeekday(const char *weekday) { strncpy(clockWeekday, weekday, MAX_CLOCK_WEEKDAY_LEN); }; void setClockHalted(bool halted) { clockHalted = halted; }; diff --git a/src/main.cpp b/src/main.cpp index 1492277..43b0f30 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -221,7 +221,7 @@ void loop(void) } if (!requestToRegisterSent) { - radio.broadcastRequestRegistration("testClock"); + radio.broadcastOfferPairing("testClock", nRF_Channel); requestToRegisterSent = true; return; } diff --git a/src/radio.cpp b/src/radio.cpp index 9fd0d12..9c40499 100644 --- a/src/radio.cpp +++ b/src/radio.cpp @@ -3,12 +3,26 @@ #include #include "config.h" #include "radio.h" -#include "clockMsg.h" static RF24 rf24(PIN_NRF24_CE, PIN_NRF24_CSN); // 10, 8 static byte addresses[][6] = {NETWORK_ADDRESS_MASTER_SEND, NETWORK_ADDRESS_MASTER_RECEIVE}; +static int sendFailedCounter = 0; +static unsigned long stopTime = 0, pauseTime = 0; +static char thisClockName[MAX_CLOCK_NAME_LEN]; + +static bool inTX=1, inRX=0, role=inRX; + +#define MAX_CLIENTS_MANAGED 20 +static uint8_t numberOfKnownClients = 0; +static struct { + uint8_t clientNetworkAddress; + char clientName[MAX_CLIENT_NAME_LEN]; +} client[MAX_CLIENTS_MANAGED]; +static uint8_t nextClientNetworkAddress = 1; + + void Radio::begin(void) { display->addLogMessage("Start RF24 radio"); pinMode(PIN_NRF24_CSN, OUTPUT); @@ -53,8 +67,6 @@ void Radio::begin(void) { rf24.startListening(); } -static bool inTX=1, inRX=0, role=inRX; - static void switchToSenderRole(RF24 radio) { Serial.println("*** master: CHANGING TO TRANSMIT ROLE"); @@ -73,21 +85,14 @@ static void switchToReceiverRole(RF24 radio) role = inRX; // Become the primary receiver (pong back) } -static int sendFailedCounter = 0; -static unsigned long stopTime = 0, pauseTime = 0; - -void Radio::broadcastRequestRegistration(const char *clockName) { - char sendBuffer[32]; - - memset(sendBuffer, 0, 32); - sendBuffer[0] = msgType_ReqReg; - strncpy(sendBuffer+1, clockName, MAX_CLOCK_NAME_LEN); - int msgLength = MAX_CLOCK_NAME_LEN + 1; - +void Radio::broadcastMessage(void *msg, int len) { switchToSenderRole(rf24); - if (!rf24.writeFast(sendBuffer, msgLength, true /*multicast*/)) { + if (!rf24.writeFast(msg, len, true /*multicast*/)) { sendFailedCounter++; - Serial.print("*** ERROR: failed to send ReqRegistration msg for "); Serial.println(clockName); + Serial.print("*** ERROR: failed to send msg type="); + Serial.print(*((char *) msg)); + Serial.print(" for to=0x"); + Serial.println(*((uint8_t *) (msg+1)), HEX); } //This is only required when NO ACK ( enableAutoAck(0) ) payloads are used if (millis() - pauseTime > 3) { @@ -103,32 +108,102 @@ void Radio::broadcastRequestRegistration(const char *clockName) { Serial.print("*** send finished, fail-counter="); Serial.println(sendFailedCounter); } +void Radio::broadcastOfferPairing(const char *clockName, uint8_t channel) { + struct offerPairing_s msg; + + memset(&msg, 0, sizeof(msg)); + msg.msgType = msgType_OfferPairing; + msg.from = MESSAGE_FROM_Master; + msg.to = MESSAGE_TO_ALL; + strncpy(msg.clockName, clockName, MAX_CLOCK_NAME_LEN); + strncpy(thisClockName, clockName, MAX_CLOCK_NAME_LEN); + msg.channel = channel; + int msgLength = sizeof(msg); + + broadcastMessage(&msg, msgLength); +} + void Radio::broadcastClock(int day, int hour, int minute, int second, int msPerModelSecond) { struct clockMsg_s clockMsg; clockMsg.msgType = msgType_Clock; + clockMsg.to = MESSAGE_TO_ALL; + clockMsg.from = MESSAGE_FROM_Master; + strncpy(clockMsg.clockName, thisClockName, MAX_CLOCK_NAME_LEN); clockMsg.day = day; clockMsg.hour = hour; clockMsg.minute = minute; clockMsg.second = second; clockMsg.speed = (msPerModelSecond >> 2) & 0xff; // divide by 4 - switchToSenderRole(rf24); - if (!rf24.writeFast(&clockMsg, sizeof(clockMsg), true /*multicast*/)) { - sendFailedCounter++; - Serial.print("*** ERROR: failed to send clock msg for "); Serial.print(hour); Serial.print(":"); Serial.print(minute); Serial.print(":"); Serial.println(second); - } - //This is only required when NO ACK ( enableAutoAck(0) ) payloads are used - if (millis() - pauseTime > 3) { - pauseTime = millis(); - rf24.txStandBy(); // Need to drop out of TX mode every 4ms if sending a steady stream of multicast data - //delayMicroseconds(130); // This gives the PLL time to sync back up - } - stopTime = millis(); - //This should be called to wait for completion and put the radio in standby mode after transmission, returns 0 if data still in FIFO (timed out), 1 if success - if (!rf24.txStandBy()) { sendFailedCounter += 3; } //Standby, block only until FIFO empty or auto-retry timeout. Flush TX FIFO if failed - //radio.txStandBy(1000); //Standby, using extended timeout period of 1 second - Serial.print("*** send finished, fail-counter="); Serial.println(sendFailedCounter); + broadcastMessage(&clockMsg, sizeof(clockMsg)); +} + +void Radio::broadcastTextMessage(const char *text) { + struct textMessage_s msg; + + memset(&msg, 0, sizeof(msg)); + msg.msgType = msgType_TextMessage; + msg.from = MESSAGE_FROM_Master; + msg.to = MESSAGE_TO_ALL; + strncpy(msg.text, text, MAX_TEXT_MESSAGE_LEN); + int msgLength = sizeof(msg); + + broadcastMessage(&msg, msgLength); +} + + +void Radio::ackPairingRequest(char *clientName, uint8_t clientNetworkAddress) { + struct ackPairing_s msg; + memset(&msg, 0, sizeof(msg)); + msg.msgType = msgType_ackPairingRequest; + msg.from = MESSAGE_FROM_Master; + msg.to = MESSAGE_TO_ALL; + strncpy(&msg.clockName[0], thisClockName, MAX_CLOCK_NAME_LEN); + strncpy(&msg.clientName[0], clientName, MAX_CLIENT_NAME_LEN); + msg.clientNetworkAddress = clientNetworkAddress; + int msgLength = sizeof(msg); + + broadcastMessage(&msg, msgLength); +} + + +// handle received messages +void Radio::handleRequestPairing(struct requestPairing_s *request) { + int i; + bool found; + + if (strncmp(&request->clientName[0], thisClockName, MAX_CLOCK_NAME_LEN) == 0) { + // add this client to client list + Serial.print("*** Rcv'd pairing request for "); Serial.println(&request->clientName[0]); + found = false; + for (int i=0; iclientName[0], MAX_CLIENT_NAME_LEN) == 0) found = true; + } + if (!found) { + // create new entry and set "i" to this new entry + if (numberOfKnownClients < MAX_CLIENTS_MANAGED) { + i = numberOfKnownClients; + strncpy(&client[i].clientName[0], &request->clientName[0], MAX_CLIENT_NAME_LEN); + client[i].clientNetworkAddress = nextClientNetworkAddress++; + numberOfKnownClients++; + Serial.print("Added new client to list of clients. Now know "); + Serial.print(numberOfKnownClients); + Serial.print(", new is "); + Serial.println(&client[i].clientName[0]); + } else { + Serial.print("ERROR: Too many client pairing requests, max reached: "); + Serial.println(MAX_CLIENTS_MANAGED); + return; + } + } + ackPairingRequest(&request->clientName[0], client[i].clientNetworkAddress); + } + // else ignore this message, as it is for another clock + else { + Serial.print("*** received pairing request, but it is for another clock named: "); + Serial.println(&request->clientName[0]); + } } void Radio::loop(void) { diff --git a/src/radio.h b/src/radio.h index 72218d1..6d4e7bb 100644 --- a/src/radio.h +++ b/src/radio.h @@ -3,15 +3,75 @@ #include "config.h" #include "display.h" +// msgType definitions to identify message and be able to use correcnt structure +#define msgType_Clock 'c' /* clock update, sent by master using broadcast */ +#define msgType_OfferPairing 'R' /* Request Registration, sent by Master using broadcast; ask clients to register */ +#define msgType_RequestPairing 'r' /* Registration message, sent by Client using 1:1 message to master */ +#define msgType_ackPairingRequest 'A' +#define msgType_TextMessage 'T' + +// address definitions for special addresses +#define MESSAGE_TO_ALL 0xff +#define MESSAGE_TO_Master 0x00 +#define MESSAGE_FROM_Master MESSAGE_TO_Master + +// message structures for easy access: +struct clockMsg_s { + uint8_t msgType; + uint8_t to; + uint8_t from; + char clockName[MAX_CLOCK_NAME_LEN]; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t speed; // msPerModelSecond / 4 --> 0..250 +}; + +struct offerPairing_s { + uint8_t msgType; + uint8_t to; + uint8_t from; + char clockName[MAX_CLOCK_NAME_LEN]; + uint8_t channel; +}; + +struct requestPairing_s { + uint8_t msgType; + uint8_t to; + uint8_t from; + char clientName[MAX_CLIENT_NAME_LEN]; +}; + +struct ackPairing_s { + uint8_t msgType; + uint8_t to; + uint8_t from; + char clockName[MAX_CLOCK_NAME_LEN]; + char clientName[MAX_CLIENT_NAME_LEN]; + uint8_t clientNetworkAddress; // address assigned to client by controller (instead of using names, used as from/to */ +}; + +struct textMessage_s { + uint8_t msgType; + uint8_t to; + uint8_t from; + char text[MAX_TEXT_MESSAGE_LEN]; +}; + class Radio { public: Radio(Display *d, bool _isMaster):display(d), isMaster(_isMaster) { }; void begin(void); void loop(void); void broadcastClock(int day, int hour, int minute, int second, int msPerModelSecond); - void broadcastRequestRegistration(const char *clockName); + void broadcastOfferPairing(const char *clockName, uint8_t channel); + void broadcastTextMessage(const char *text); + void handleRequestPairing(struct requestPairing_s *request); + void ackPairingRequest(char *clientName, uint8_t clientNetworkAddress); private: Display *display; bool isMaster; + void broadcastMessage(void *msg, int len); }; #endif