Implemented most of the messages.

This commit is contained in:
Dirk Jahnke 2018-11-18 15:51:03 +01:00
parent 6eeb9177f9
commit f6849ea534
7 changed files with 208 additions and 53 deletions

View File

@ -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

View File

@ -1,18 +0,0 @@
#ifndef clockMsg_h_included
#define clockMsg_h_included
#include <Arduino.h>
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

View File

@ -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

View File

@ -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; };

View File

@ -221,7 +221,7 @@ void loop(void)
}
if (!requestToRegisterSent) {
radio.broadcastRequestRegistration("testClock");
radio.broadcastOfferPairing("testClock", nRF_Channel);
requestToRegisterSent = true;
return;
}

View File

@ -3,12 +3,26 @@
#include <RF24.h>
#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; i<numberOfKnownClients && !found; ++i) {
if (strncmp(&client[i].clientName[0], &request->clientName[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) {

View File

@ -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