Implemented most of the messages.
This commit is contained in:
parent
6eeb9177f9
commit
f6849ea534
37
README.md
37
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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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; };
|
||||
|
|
|
@ -221,7 +221,7 @@ void loop(void)
|
|||
}
|
||||
|
||||
if (!requestToRegisterSent) {
|
||||
radio.broadcastRequestRegistration("testClock");
|
||||
radio.broadcastOfferPairing("testClock", nRF_Channel);
|
||||
requestToRegisterSent = true;
|
||||
return;
|
||||
}
|
||||
|
|
139
src/radio.cpp
139
src/radio.cpp
|
@ -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) {
|
||||
|
|
62
src/radio.h
62
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
|
||||
|
|
Loading…
Reference in New Issue