229 lines
7.7 KiB
Plaintext
229 lines
7.7 KiB
Plaintext
|
//
|
||
|
// FILE: ClockServer.cpp
|
||
|
// PURPOSE: UDP broadcast sender for fastclock (FREMO clock)
|
||
|
//
|
||
|
//
|
||
|
|
||
|
#include "ClockServer.h"
|
||
|
#include <ESP8266WiFi.h>
|
||
|
#include <WiFiUdp.h>
|
||
|
#include <DjFastclockScanner.h>
|
||
|
|
||
|
|
||
|
#ifdef ESP8266
|
||
|
extern "C" {
|
||
|
#include "user_interface.h"
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
const char * const PROGMEM clockServerConfig[] = {
|
||
|
"clockServerName:string:DefaultClock",
|
||
|
"waitBeforeStartingClockServer_ms:int:5000",
|
||
|
"timeToCollectOtherClocks_ms:int:20000",
|
||
|
"autoActivateAfter_ms:int:0",
|
||
|
"sendClockUpdateEvery_ms:int:2000",
|
||
|
"ipMulticast:string:239.50.50.20",
|
||
|
"clientListenPort:int:2000",
|
||
|
};
|
||
|
|
||
|
static WiFiUDP udp;
|
||
|
|
||
|
#define CLOCK_PACKET_SIZE 1024
|
||
|
static byte packetBuffer[CLOCK_PACKET_SIZE+1]; //buffer to hold outgoing packets
|
||
|
|
||
|
String ClockServer::name{""};
|
||
|
IPAddress ClockServer::ipMulticast; //.fromString("239.50.50.20");
|
||
|
int ClockServer::clientListenPort{2000};
|
||
|
String ClockServer::text{""};
|
||
|
boolean ClockServer::active{false};
|
||
|
float ClockServer::clockSpeed{1.0};
|
||
|
String ClockServer::clockString{""};
|
||
|
String ClockServer::weekdayName{""};
|
||
|
int ClockServer::weekday{0};
|
||
|
int ClockServer::clockHours{0};
|
||
|
int ClockServer::clockMinutes{0};
|
||
|
int ClockServer::clockSeconds{0};
|
||
|
ClockServerStatus ClockServer::serverStatus = ClockServerStatus::ServerNotStarted;
|
||
|
uint32_t ClockServer::nextServerStatusTime;
|
||
|
int ClockServer::sendClockUpdateEvery_ms;
|
||
|
int ClockServer::autoActivateAfter_ms;
|
||
|
|
||
|
void ClockServer::begin() {
|
||
|
config.loadFile("clockserver.cfg", clockServerConfig, sizeof(clockServerConfig)/sizeof(clockServerConfig[0]));
|
||
|
name = config.getString("clockServerName");
|
||
|
ipMulticast.fromString(config.getString("ipMulticast"));
|
||
|
clientListenPort = config.getInt("clientListenPort");
|
||
|
|
||
|
sendClockUpdateEvery_ms = config.getInt("sendClockUpdateEvery_ms");
|
||
|
autoActivateAfter_ms = config.getInt("autoActivateAfter_ms");
|
||
|
nextServerStatusTime = millis() + config.getInt("waitBeforeStartingClockServer_ms");
|
||
|
logHeap();
|
||
|
}
|
||
|
|
||
|
void ClockServer::clockTick() {
|
||
|
if (ClockServer::active == false) return;
|
||
|
//Debug::outln(F("> Tick"));
|
||
|
ClockServer::clockSeconds++;
|
||
|
if (ClockServer::clockSeconds >= 60) {
|
||
|
ClockServer::clockSeconds = 0;
|
||
|
ClockServer::clockMinutes++;
|
||
|
if (ClockServer::clockMinutes >= 60) {
|
||
|
ClockServer::clockMinutes = 0;
|
||
|
ClockServer::clockHours++;
|
||
|
if (ClockServer::clockHours >= 24) {
|
||
|
ClockServer::clockHours = 0;
|
||
|
weekday++;
|
||
|
if (weekday >= 7) {
|
||
|
weekday = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void clockTick() {
|
||
|
ClockServer::clockTick();
|
||
|
}
|
||
|
|
||
|
void ClockServer::activateClock() {
|
||
|
active = true;
|
||
|
if (clockSpeed < 0.1) {
|
||
|
clockSpeed = 0.1;
|
||
|
debug.outln(F("ERROR: clockSpeed was too small, set to 0.1 which still is very small"));
|
||
|
}
|
||
|
clockTrigger.attach_ms(1000.0 / clockSpeed, clockTick);
|
||
|
logHeap();
|
||
|
}
|
||
|
|
||
|
void ClockServer::deactivateClock() {
|
||
|
active = false;
|
||
|
clockTrigger.detach();
|
||
|
logHeap();
|
||
|
}
|
||
|
|
||
|
void ClockServer::setSpeed(float newSpeed) {
|
||
|
clockSpeed = newSpeed;
|
||
|
if (active) {
|
||
|
deactivateClock();
|
||
|
activateClock();
|
||
|
}
|
||
|
logHeap();
|
||
|
}
|
||
|
|
||
|
void ClockServer::sendFastclockMessage() {
|
||
|
IPAddress ip = WiFi.localIP();
|
||
|
static String nl = String("\r\n");
|
||
|
String msg = String("fastclock") + nl + "version=2" + nl;
|
||
|
msg += "name=" + name + nl;
|
||
|
msg += "ip-address=" + ip.toString() + nl;
|
||
|
msg += "ip-port=" + String(clientListenPort) + nl;
|
||
|
msg += "text=" + text + nl;
|
||
|
msg += "clocktype=fastclock" + nl;
|
||
|
msg += "active=" + (active?String("yes"):String("no")) + nl;
|
||
|
msg += "speed=" + String(clockSpeed) + nl;
|
||
|
msg += "clock=" + String(clockHours) + ":" + String(clockMinutes) + ":" + String(clockSeconds) + nl;
|
||
|
msg += "weekday=" + String(weekday) + nl;
|
||
|
|
||
|
uint8_t result = udp.beginPacketMulticast(ipMulticast, clientListenPort, ip);
|
||
|
debug.outln(result?"udp.begin":"ERROR on udp.begin");
|
||
|
size_t sentBytes = udp.write(msg.c_str(),msg.length());
|
||
|
debug.outln(String(sentBytes) + " bytes sent"); //,sizeof(msg));
|
||
|
result = udp.endPacket();
|
||
|
debug.outln(result?"send was OK":"send FAILED");
|
||
|
debug.out(F("*** ")); debug.outln(msg);
|
||
|
|
||
|
delay(1);
|
||
|
}
|
||
|
|
||
|
void ClockServer::loop() {
|
||
|
static String *knownClocks;
|
||
|
|
||
|
switch (serverStatus) {
|
||
|
case ClockServerStatus::ServerNotStarted:
|
||
|
if (millis() > nextServerStatusTime) {
|
||
|
serverStatus = ClockServerStatus::CollectingExistingClocks;
|
||
|
nextServerStatusTime = millis() + config.getInt("timeToCollectOtherClocks_ms");
|
||
|
if (!udp.beginMulticast(WiFi.localIP(), ipMulticast, clientListenPort)) {
|
||
|
debug.outln(F("ERROR: failed to begin UDP"));
|
||
|
debug.out(F("local IP=")); debug.out(WiFi.localIP().toString());
|
||
|
debug.out(F(", multicast IP=")); debug.out(ipMulticast.toString());
|
||
|
debug.out(F(", Port=")); debug.outln(clientListenPort);
|
||
|
}
|
||
|
|
||
|
logHeap();
|
||
|
}
|
||
|
break;
|
||
|
case ClockServerStatus::CollectingExistingClocks:
|
||
|
// do nothing, we wait for the client to collect the clocks for us
|
||
|
if (millis() > nextServerStatusTime) {
|
||
|
serverStatus = ClockServerStatus::StartingServer;
|
||
|
nextServerStatusTime = millis()-1; // be sure, this will go through the next section on next loop() call
|
||
|
logHeap();
|
||
|
} else {
|
||
|
int length = udp.parsePacket();
|
||
|
if (length > 0) {
|
||
|
// debug.out(F("ClockServer received: ")); debug.out(length); debug.outln(F(" bytes."));
|
||
|
udp.read(packetBuffer, CLOCK_PACKET_SIZE);
|
||
|
packetBuffer[length] = '\0';
|
||
|
|
||
|
// interpret Clock Message
|
||
|
// debug.out(F("> ")); debug.outln( (char *) packetBuffer);
|
||
|
String msg = String((char *) packetBuffer);
|
||
|
if (!msg.startsWith("fastclock\r\n")) {
|
||
|
debug.out(F("ERROR: This is not a fastclock message! Got message=")); debug.outln(msg.substring(0,30));
|
||
|
}
|
||
|
msg = msg.substring(11);
|
||
|
if (!msg.startsWith("version=2\r\n")) {
|
||
|
debug.out(F("WARNING: Version of fastclock not supported! Got ")); debug.outln(msg.substring(0,10));
|
||
|
}
|
||
|
msg = msg.substring(msg.indexOf('\n')+1);
|
||
|
|
||
|
String checkName{""};
|
||
|
if (msg.startsWith("name=")) {
|
||
|
checkName = msg.substring(msg.indexOf('=')+1, msg.indexOf('\r'));
|
||
|
fastclockScanner.addClock(checkName);
|
||
|
} else { debug.outln(F("ERROR: Clock Message Format invalid! Expected name field.")); return; }
|
||
|
msg = String(""); // release string
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case ClockServerStatus::StartingServer:
|
||
|
// debug: output the clocks seen during collectingExistingClocks time
|
||
|
knownClocks = fastclockScanner.getKnownClocks();
|
||
|
debug.outln(F("--- Following Clocks have been seen during bootup: ---"));
|
||
|
for (int i=0; i<fastclockScanner.getNumberOfKnownClocks(); ++i) {
|
||
|
debug.outln(knownClocks[i]);
|
||
|
}
|
||
|
debug.outln(F("--- end of list of known clocks"));
|
||
|
delay(10);
|
||
|
if (autoActivateAfter_ms > 0) {
|
||
|
nextServerStatusTime = millis() + autoActivateAfter_ms;
|
||
|
serverStatus = ClockServerStatus::WaitForAutoActivate;
|
||
|
} else {
|
||
|
serverStatus = ClockServerStatus::ServerStarted;
|
||
|
nextServerStatusTime = millis() + sendClockUpdateEvery_ms;
|
||
|
}
|
||
|
break;
|
||
|
case ClockServerStatus::WaitForAutoActivate:
|
||
|
if (millis() > nextServerStatusTime) {
|
||
|
serverStatus = ClockServerStatus::ServerStarted;
|
||
|
activateClock();
|
||
|
logHeap();
|
||
|
}
|
||
|
break;
|
||
|
case ClockServerStatus::ServerStarted:
|
||
|
if (millis() > nextServerStatusTime) {
|
||
|
if (active) sendFastclockMessage();
|
||
|
nextServerStatusTime = millis() + sendClockUpdateEvery_ms;
|
||
|
logHeap();
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
// this is an error case
|
||
|
debug.outln(F("ERROR: Unknown server state in ClockServer.cpp. Resetting to ServerNotStarted."));
|
||
|
serverStatus = ClockServerStatus::ServerNotStarted;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|