2019-01-28 21:47:44 +01:00
|
|
|
|
/* NOTE:
|
|
|
|
|
The IF482 Generator needs an high precise 1 Hz clock signal which cannot be
|
|
|
|
|
acquired in suitable precision on the ESP32 SoC itself. Additional clocking
|
|
|
|
|
hardware is required, ususally the clock signal is generated by external RTC or
|
2019-01-29 09:04:31 +01:00
|
|
|
|
GPS which can generate a precise time pulse signal (+/- 2ppm).
|
|
|
|
|
|
|
|
|
|
In this example code we use a Maxim DS3231 RTC chip, and configure the chips's
|
|
|
|
|
interrupt output pin as clock. The clock signal triggers an interrupt on the
|
|
|
|
|
ESP32, which controls the realtime output of IF482 telegram. This is why code in
|
2019-01-28 21:47:44 +01:00
|
|
|
|
IF482.cpp depends on code in RTCTIME.cpp.
|
|
|
|
|
*/
|
2019-01-25 22:49:26 +01:00
|
|
|
|
|
2019-01-28 21:47:44 +01:00
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
/*
|
2019-01-26 12:32:58 +01:00
|
|
|
|
IF482 Generator to control clocks with IF482 telegram input (e.g. BÜRK BU190)
|
2019-01-20 22:39:26 +01:00
|
|
|
|
|
2019-01-29 09:04:31 +01:00
|
|
|
|
|
2019-01-26 12:32:58 +01:00
|
|
|
|
Example IF482 telegram: "OAL160806F170400"
|
2019-01-20 22:39:26 +01:00
|
|
|
|
|
|
|
|
|
IF482 Specification:
|
2019-01-26 12:32:58 +01:00
|
|
|
|
http://www.mobatime.com/fileadmin/user_upload/downloads/TE-112023.pdf
|
|
|
|
|
|
|
|
|
|
The IF 482 telegram is a time telegram, which sends the time and date
|
|
|
|
|
information as ASCII characters through the serial interface RS 232 or RS 422.
|
|
|
|
|
|
|
|
|
|
Communication parameters:
|
|
|
|
|
|
|
|
|
|
Baud rate: 9600 Bit/s
|
|
|
|
|
Data bits 7
|
|
|
|
|
Parity: even
|
|
|
|
|
Stop bit: 1
|
|
|
|
|
Jitter: < 50ms
|
|
|
|
|
|
|
|
|
|
Interface : RS232 or RS422
|
|
|
|
|
|
|
|
|
|
Synchronization: Telegram ends at the beginning of the second
|
|
|
|
|
specified in the telegram
|
|
|
|
|
|
|
|
|
|
Cycle: 1 second
|
|
|
|
|
|
|
|
|
|
Format of ASCII telegram string:
|
|
|
|
|
|
|
|
|
|
Byte Meaning ASCII Hex
|
|
|
|
|
1 Start of telegram O 4F
|
|
|
|
|
2 Monitoring* A 41
|
|
|
|
|
3 Time-Season** W/S/U/L 57 or 53
|
|
|
|
|
4 Year tens 0 .. 9 30 .. 39
|
|
|
|
|
5 Year unit 0 .. 9 30 .. 39
|
|
|
|
|
6 Month tens 0 or 1 30 or 31
|
|
|
|
|
7 Month unit 0 .. 9 30 .. 39
|
|
|
|
|
8 Day tens 0 .. 3 30 .. 33
|
|
|
|
|
9 Day unit 0 .. 9 30 .. 39
|
|
|
|
|
10 Day of week*** 1 .. 7 31 .. 37
|
|
|
|
|
11 Hours tens 0 .. 2 30 .. 32
|
|
|
|
|
12 Hours unit 0 .. 9 30 .. 39
|
|
|
|
|
13 Minutes tens 0 .. 5 30 .. 35
|
|
|
|
|
14 Minutes unit 0 .. 9 30 .. 39
|
|
|
|
|
15 Seconds tens 0 .. 5 30 .. 35
|
|
|
|
|
16 Seconds unit 0 .. 9 30 .. 39
|
|
|
|
|
17 End of telegram CR 0D
|
|
|
|
|
|
|
|
|
|
*) Monitoring:
|
|
|
|
|
With a correctly received time in the sender unit, the ASCII character 'A' is
|
|
|
|
|
issued. If 'M' is issued, this indicates that the sender was unable to receive
|
|
|
|
|
any time signal for over 12 hours (time is accepted with ‘A’ and ‘M’).
|
|
|
|
|
|
|
|
|
|
**) Season:
|
|
|
|
|
W: Standard time,
|
|
|
|
|
S: Season time,
|
|
|
|
|
U: UTC time (not supported by all systems),
|
|
|
|
|
L: Local Time
|
|
|
|
|
|
|
|
|
|
***) Day of week:
|
|
|
|
|
not evaluated by model BU-190
|
2019-01-25 22:49:26 +01:00
|
|
|
|
|
2019-01-20 22:39:26 +01:00
|
|
|
|
*/
|
2019-01-28 21:47:44 +01:00
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2019-01-20 22:39:26 +01:00
|
|
|
|
|
2019-02-08 22:19:44 +01:00
|
|
|
|
#ifdef HAS_IF482
|
|
|
|
|
|
|
|
|
|
#ifdef HAS_DCF77
|
2019-02-09 15:46:44 +01:00
|
|
|
|
#error You must define at most one of IF482 or DCF77!
|
2019-02-08 22:19:44 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
2019-01-20 22:39:26 +01:00
|
|
|
|
#include "if482.h"
|
|
|
|
|
|
|
|
|
|
// Local logging tag
|
|
|
|
|
static const char TAG[] = "main";
|
|
|
|
|
|
2019-02-07 23:05:26 +01:00
|
|
|
|
#define IF482_FRAME_SIZE (17)
|
|
|
|
|
#define IF482_PULSE_DURATION (1000)
|
2019-02-08 23:08:14 +01:00
|
|
|
|
|
2019-02-09 13:02:38 +01:00
|
|
|
|
// select internal / external clock
|
|
|
|
|
#if defined RTC_INT && defined RTC_CLK
|
2019-02-08 23:08:14 +01:00
|
|
|
|
#define PPS RTC_CLK
|
2019-02-09 13:02:38 +01:00
|
|
|
|
#elif defined GPS_INT && defined GPS_CLK
|
|
|
|
|
#define PPS GPS_CLK
|
2019-02-08 22:19:44 +01:00
|
|
|
|
#else
|
|
|
|
|
#define PPS IF482_PULSE_DURATION
|
|
|
|
|
#endif
|
2019-01-27 18:19:25 +01:00
|
|
|
|
|
|
|
|
|
HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS)
|
2019-01-25 22:49:26 +01:00
|
|
|
|
|
2019-02-04 21:42:44 +01:00
|
|
|
|
// initialize and configure IF482 Generator
|
2019-01-27 18:19:25 +01:00
|
|
|
|
int if482_init(void) {
|
2019-01-25 22:49:26 +01:00
|
|
|
|
|
2019-02-07 23:05:26 +01:00
|
|
|
|
// open serial interface
|
|
|
|
|
IF482.begin(HAS_IF482);
|
2019-02-10 16:43:24 +01:00
|
|
|
|
// setup timepulse
|
|
|
|
|
timepulse_init(PPS);
|
2019-02-04 21:42:44 +01:00
|
|
|
|
|
2019-02-07 07:32:55 +01:00
|
|
|
|
// start if482 serial output feed task
|
|
|
|
|
xTaskCreatePinnedToCore(if482_loop, // task function
|
|
|
|
|
"if482loop", // name of task
|
|
|
|
|
2048, // stack size of task
|
|
|
|
|
(void *)1, // parameter of the task
|
2019-02-11 23:37:45 +01:00
|
|
|
|
4, // priority of the task
|
2019-02-07 23:05:26 +01:00
|
|
|
|
&ClockTask, // task handle
|
2019-02-07 07:32:55 +01:00
|
|
|
|
0); // CPU core
|
|
|
|
|
|
2019-02-07 23:05:26 +01:00
|
|
|
|
assert(ClockTask); // has clock task started?
|
2019-02-11 23:37:45 +01:00
|
|
|
|
// timepulse_start(); // start pulse
|
2019-01-27 18:19:25 +01:00
|
|
|
|
|
2019-02-07 23:05:26 +01:00
|
|
|
|
return 1; // success
|
2019-01-25 22:49:26 +01:00
|
|
|
|
} // if482_init
|
|
|
|
|
|
2019-02-07 07:32:55 +01:00
|
|
|
|
String IF482_Out(time_t tt) {
|
2019-02-02 09:15:31 +01:00
|
|
|
|
|
2019-02-11 23:37:45 +01:00
|
|
|
|
time_t t = 1 + myTZ.toLocal(tt);
|
2019-02-07 23:05:26 +01:00
|
|
|
|
char mon, buf[14], out[IF482_FRAME_SIZE];
|
2019-01-25 22:49:26 +01:00
|
|
|
|
|
|
|
|
|
switch (timeStatus()) { // indicates if time has been set and recently synced
|
2019-01-27 18:19:25 +01:00
|
|
|
|
case timeSet: // time is set and is synced
|
2019-01-26 18:49:53 +01:00
|
|
|
|
mon = 'A';
|
2019-01-25 22:49:26 +01:00
|
|
|
|
break;
|
|
|
|
|
case timeNeedsSync: // time had been set but sync attempt did not succeed
|
2019-01-26 18:49:53 +01:00
|
|
|
|
mon = 'M';
|
2019-01-25 22:49:26 +01:00
|
|
|
|
break;
|
|
|
|
|
default: // time not set, no valid time
|
2019-01-26 18:49:53 +01:00
|
|
|
|
mon = '?';
|
2019-01-25 22:49:26 +01:00
|
|
|
|
break;
|
|
|
|
|
} // switch
|
|
|
|
|
|
2019-02-04 21:42:44 +01:00
|
|
|
|
// do we have confident time/date?
|
|
|
|
|
if ((timeStatus() == timeSet) || (timeStatus() == timeNeedsSync))
|
|
|
|
|
snprintf(buf, sizeof(buf), "%02u%02u%02u%1u%02u%02u%02u", year(t) - 2000,
|
2019-01-27 18:19:25 +01:00
|
|
|
|
month(t), day(t), weekday(t), hour(t), minute(t), second(t));
|
2019-02-04 21:42:44 +01:00
|
|
|
|
else
|
|
|
|
|
snprintf(buf, sizeof(buf), "000000F000000"); // no confident time/date
|
2019-01-25 22:49:26 +01:00
|
|
|
|
|
2019-02-04 21:42:44 +01:00
|
|
|
|
// output IF482 telegram
|
|
|
|
|
snprintf(out, sizeof(out), "O%cL%s\r", mon, buf);
|
2019-02-04 20:02:30 +01:00
|
|
|
|
ESP_LOGD(TAG, "IF482 = %s", out);
|
2019-01-25 22:49:26 +01:00
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-27 18:19:25 +01:00
|
|
|
|
void if482_loop(void *pvParameters) {
|
|
|
|
|
|
|
|
|
|
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
|
|
|
|
|
2019-02-11 23:37:45 +01:00
|
|
|
|
time_t tOut;
|
2019-01-27 18:19:25 +01:00
|
|
|
|
TickType_t wakeTime;
|
2019-02-11 23:37:45 +01:00
|
|
|
|
const TickType_t tTx = tx_time(HAS_IF482); // duration of telegram transmit
|
|
|
|
|
BitsPending = true; // start blink in display
|
2019-01-27 18:19:25 +01:00
|
|
|
|
|
2019-02-11 23:37:45 +01:00
|
|
|
|
// phase 1: sync task on top of second
|
2019-01-27 18:19:25 +01:00
|
|
|
|
|
2019-02-11 23:37:45 +01:00
|
|
|
|
sync_clock(now());
|
2019-01-27 18:19:25 +01:00
|
|
|
|
|
2019-02-11 23:37:45 +01:00
|
|
|
|
const TickType_t t0 = xTaskGetTickCount(); // moment of start top of second
|
|
|
|
|
|
|
|
|
|
timepulse_start(); // start timepulse
|
|
|
|
|
|
|
|
|
|
xTaskNotifyWait(
|
|
|
|
|
0x00, // don't clear any bits on entry
|
|
|
|
|
ULONG_MAX, // clear all bits on exit
|
|
|
|
|
&wakeTime, // receives moment of call from isr
|
|
|
|
|
portMAX_DELAY); // wait forever (missing error handling here...)
|
|
|
|
|
|
|
|
|
|
const TickType_t tOffset = wakeTime - t0;
|
|
|
|
|
const TickType_t tShot =
|
|
|
|
|
(tOffset < tTx) ? (1000 - tOffset - tTx) : (tOffset - tTx);
|
|
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "IF482 signal synced with precision %dms", 1000 - tOffset);
|
|
|
|
|
|
|
|
|
|
// phase 2: sync task on time pulse interrupt
|
2019-01-27 18:19:25 +01:00
|
|
|
|
for (;;) {
|
|
|
|
|
xTaskNotifyWait(
|
|
|
|
|
0x00, // don't clear any bits on entry
|
|
|
|
|
ULONG_MAX, // clear all bits on exit
|
|
|
|
|
&wakeTime, // receives moment of call from isr
|
|
|
|
|
portMAX_DELAY); // wait forever (missing error handling here...)
|
|
|
|
|
|
2019-02-11 23:37:45 +01:00
|
|
|
|
tOut = now() + 1; // next second after waketime
|
|
|
|
|
|
2019-02-09 13:02:38 +01:00
|
|
|
|
// select clock scale
|
2019-02-08 23:08:14 +01:00
|
|
|
|
#if (PPS == IF482_PULSE_DURATION) // we don't need clock rescaling
|
2019-02-07 23:11:10 +01:00
|
|
|
|
// wait until it's time to start transmit telegram for next second
|
2019-02-11 23:37:45 +01:00
|
|
|
|
vTaskDelayUntil(&wakeTime, tShot); // sets waketime to moment of tShot
|
|
|
|
|
IF482.print(IF482_Out(tOut));
|
2019-02-09 13:02:38 +01:00
|
|
|
|
|
2019-02-08 23:08:14 +01:00
|
|
|
|
#elif (PPS > IF482_PULSE_DURATION) // we need upclocking
|
|
|
|
|
for (uint8_t i = 1; i <= PPS / IF482_PULSE_DURATION; i++) {
|
2019-02-11 23:37:45 +01:00
|
|
|
|
vTaskDelayUntil(&wakeTime, tShot); // sets waketime to moment of shot
|
|
|
|
|
IF482.print(IF482_Out(tOut));
|
2019-02-08 23:08:14 +01:00
|
|
|
|
}
|
2019-02-09 13:02:38 +01:00
|
|
|
|
|
2019-02-09 15:46:44 +01:00
|
|
|
|
#elif (PPS < IF482_PULSE_DURATION) // we need downclocking, not yet implemented
|
|
|
|
|
#error Timepulse is too low for IF482!
|
2019-02-07 23:05:26 +01:00
|
|
|
|
#endif
|
2019-02-11 23:37:45 +01:00
|
|
|
|
} // forever
|
|
|
|
|
|
2019-01-27 18:19:25 +01:00
|
|
|
|
} // if482_loop()
|
|
|
|
|
|
2019-02-10 16:43:24 +01:00
|
|
|
|
// helper function to calculate IF482 telegram serial tx time from serial
|
|
|
|
|
// settings
|
|
|
|
|
TickType_t tx_time(unsigned long baud, uint32_t config, int8_t rxPin,
|
|
|
|
|
int8_t txPins) {
|
|
|
|
|
|
|
|
|
|
uint32_t datenbits = ((config & 0x0c) >> 2) + 5;
|
|
|
|
|
uint32_t startbits = ((config & 0x20) >> 5) + 1;
|
|
|
|
|
return pdMS_TO_TICKS(
|
|
|
|
|
round(((datenbits + startbits + 1) * IF482_FRAME_SIZE * 1000.0 / baud)));
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-20 22:39:26 +01:00
|
|
|
|
#endif // HAS_IF482
|