v1.7..152: DCF77 fixes (experimental)
This commit is contained in:
parent
39e2df7a05
commit
17cd82da68
@ -15,10 +15,6 @@
|
|||||||
#include "rtctime.h"
|
#include "rtctime.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_DCF77
|
|
||||||
#include "dcf77.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void doHousekeeping(void);
|
void doHousekeeping(void);
|
||||||
uint64_t uptime(void);
|
uint64_t uptime(void);
|
||||||
void reset_counters(void);
|
void reset_counters(void);
|
||||||
|
@ -102,6 +102,7 @@ extern uint16_t volatile macs_total, macs_wifi, macs_ble,
|
|||||||
batt_voltage; // display values
|
batt_voltage; // display values
|
||||||
extern hw_timer_t *sendCycle, *displaytimer;
|
extern hw_timer_t *sendCycle, *displaytimer;
|
||||||
extern SemaphoreHandle_t I2Caccess;
|
extern SemaphoreHandle_t I2Caccess;
|
||||||
|
extern bool volatile BitsPending;
|
||||||
|
|
||||||
extern std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
|
extern std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
|
||||||
extern std::array<uint64_t, 0xff>::iterator it;
|
extern std::array<uint64_t, 0xff>::iterator it;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
; ---> SELECT TARGET PLATFORM HERE! <---
|
; ---> SELECT TARGET PLATFORM HERE! <---
|
||||||
[platformio]
|
[platformio]
|
||||||
;env_default = generic
|
env_default = generic
|
||||||
;env_default = ebox
|
;env_default = ebox
|
||||||
;env_default = eboxtube
|
;env_default = eboxtube
|
||||||
;env_default = heltec
|
;env_default = heltec
|
||||||
@ -24,16 +24,16 @@
|
|||||||
;env_default = lolin32lora
|
;env_default = lolin32lora
|
||||||
;env_default = lolin32lite
|
;env_default = lolin32lite
|
||||||
;env_default = octopus32
|
;env_default = octopus32
|
||||||
env_default = ebox, eboxtube, heltec, ttgobeam, lopy4, lopy, ttgov21old, ttgov21new, ttgofox
|
;env_default = ebox, eboxtube, heltec, ttgobeam, lopy4, lopy, ttgov21old, ttgov21new, ttgofox
|
||||||
;
|
;
|
||||||
description = Paxcounter is a proof-of-concept ESP32 device for metering passenger flows in realtime. It counts how many mobile devices are around.
|
description = Paxcounter is a proof-of-concept ESP32 device for metering passenger flows in realtime. It counts how many mobile devices are around.
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
||||||
release_version = 1.7.143
|
release_version = 1.7.152
|
||||||
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
||||||
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
||||||
debug_level = 3
|
debug_level = 0
|
||||||
; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA
|
; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA
|
||||||
upload_protocol = esptool
|
upload_protocol = esptool
|
||||||
;upload_protocol = custom
|
;upload_protocol = custom
|
||||||
@ -45,9 +45,9 @@ monitor_speed = 115200
|
|||||||
lib_deps_lora =
|
lib_deps_lora =
|
||||||
MCCI LoRaWAN LMIC library@^2.3.1
|
MCCI LoRaWAN LMIC library@^2.3.1
|
||||||
lib_deps_display =
|
lib_deps_display =
|
||||||
U8g2@>=2.25.5
|
U8g2@>=2.25.7
|
||||||
lib_deps_rgbled =
|
lib_deps_rgbled =
|
||||||
SmartLeds@>=1.1.3
|
SmartLeds@>=1.1.5
|
||||||
lib_deps_gps =
|
lib_deps_gps =
|
||||||
TinyGPSPlus@>=1.0.2
|
TinyGPSPlus@>=1.0.2
|
||||||
lib_deps_rtc =
|
lib_deps_rtc =
|
||||||
|
@ -83,11 +83,6 @@ void doHousekeeping() {
|
|||||||
bme_status.temperature, bme_status.iaq, bme_status.iaq_accuracy);
|
bme_status.temperature, bme_status.iaq, bme_status.iaq_accuracy);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// generate DCF77 timeframes
|
|
||||||
#ifdef HAS_DCF77
|
|
||||||
sendDCF77();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// check free heap memory
|
// check free heap memory
|
||||||
if (ESP.getMinFreeHeap() <= MEM_LOW) {
|
if (ESP.getMinFreeHeap() <= MEM_LOW) {
|
||||||
ESP_LOGI(TAG,
|
ESP_LOGI(TAG,
|
||||||
|
209
src/dcf77.cpp
209
src/dcf77.cpp
@ -1,13 +1,10 @@
|
|||||||
//
|
|
||||||
// source:
|
|
||||||
// https://www.elektormagazine.com/labs/dcf77-emulator-with-esp8266-elektor-labs-version-150713
|
|
||||||
//
|
|
||||||
/*
|
/*
|
||||||
Simulate a DCF77 radio receiver
|
// Emulate a DCF77 radio receiver
|
||||||
Emit a complete three minute pulses train from the GPIO output
|
//
|
||||||
the train is preceded by a single pulse and the lacking 59th pulse to allow
|
// parts of this code werde adapted from source:
|
||||||
some clock model syncronization of the beginning frame. After the three pulses
|
//
|
||||||
train one more single pulse is sent to safely close the frame
|
https://www.elektormagazine.com/labs/dcf77-emulator-with-esp8266-elektor-labs-version-150713
|
||||||
|
//
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined HAS_DCF77
|
#if defined HAS_DCF77
|
||||||
@ -18,11 +15,9 @@
|
|||||||
static const char TAG[] = "main";
|
static const char TAG[] = "main";
|
||||||
|
|
||||||
TaskHandle_t DCF77Task;
|
TaskHandle_t DCF77Task;
|
||||||
QueueHandle_t DCFSendQueue;
|
|
||||||
hw_timer_t *dcfCycle = NULL;
|
hw_timer_t *dcfCycle = NULL;
|
||||||
|
|
||||||
#define DCF77_FRAME_SIZE 60
|
#define DCF77_FRAME_SIZE 60
|
||||||
#define DCF_FRAME_QUEUE_SIZE (HOMECYCLE / 60 + 1)
|
|
||||||
|
|
||||||
// array of dcf pulses for three minutes
|
// array of dcf pulses for three minutes
|
||||||
uint8_t DCFtimeframe[DCF77_FRAME_SIZE];
|
uint8_t DCFtimeframe[DCF77_FRAME_SIZE];
|
||||||
@ -30,84 +25,31 @@ uint8_t DCFtimeframe[DCF77_FRAME_SIZE];
|
|||||||
// initialize and configure DCF77 output
|
// initialize and configure DCF77 output
|
||||||
int dcf77_init(void) {
|
int dcf77_init(void) {
|
||||||
|
|
||||||
DCFSendQueue = xQueueCreate(DCF_FRAME_QUEUE_SIZE,
|
|
||||||
sizeof(DCFtimeframe) / sizeof(DCFtimeframe[0]));
|
|
||||||
if (!DCFSendQueue) {
|
|
||||||
ESP_LOGE(TAG, "Could not create DCF77 send queue. Aborting.");
|
|
||||||
return 0; // failure
|
|
||||||
}
|
|
||||||
ESP_LOGI(TAG, "DCF77 send queue created, size %d Bytes",
|
|
||||||
DCF_FRAME_QUEUE_SIZE * sizeof(DCFtimeframe) /
|
|
||||||
sizeof(DCFtimeframe[0]));
|
|
||||||
|
|
||||||
pinMode(HAS_DCF77, OUTPUT);
|
pinMode(HAS_DCF77, OUTPUT);
|
||||||
digitalWrite(HAS_DCF77, LOW);
|
digitalWrite(HAS_DCF77, HIGH);
|
||||||
|
|
||||||
|
xTaskCreatePinnedToCore(dcf77_loop, // task function
|
||||||
|
"dcf77loop", // name of task
|
||||||
|
2048, // stack size of task
|
||||||
|
(void *)1, // parameter of the task
|
||||||
|
3, // priority of the task
|
||||||
|
&DCF77Task, // task handle
|
||||||
|
0); // CPU core
|
||||||
|
|
||||||
|
assert(DCF77Task); // has dcf77 task started?
|
||||||
|
|
||||||
|
// setup 100ms clock signal for DCF77 generator using esp32 hardware timer 1
|
||||||
|
ESP_LOGD(TAG, "Starting DCF pulse...");
|
||||||
|
dcfCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec
|
||||||
|
timerAttachInterrupt(dcfCycle, &DCF77IRQ, true);
|
||||||
|
timerAlarmWrite(dcfCycle, 2000, true); // 100ms cycle
|
||||||
|
timerAlarmEnable(dcfCycle);
|
||||||
|
xTaskNotify(DCF77Task, 0, eNoAction);
|
||||||
|
|
||||||
return 1; // success
|
return 1; // success
|
||||||
|
|
||||||
} // ifdcf77_init
|
} // ifdcf77_init
|
||||||
|
|
||||||
// called every 100msec for DCF77 output
|
|
||||||
void DCF_Ticker() {
|
|
||||||
|
|
||||||
static uint8_t DCF_Frame[DCF77_FRAME_SIZE];
|
|
||||||
static uint8_t bit = 0;
|
|
||||||
static uint8_t pulse = 0;
|
|
||||||
static bool BitsPending = false;
|
|
||||||
|
|
||||||
while (BitsPending) {
|
|
||||||
switch (pulse++) {
|
|
||||||
|
|
||||||
case 0: // start of second -> start of timeframe for logic signal
|
|
||||||
if (DCF_Frame[bit] != dcf_off)
|
|
||||||
digitalWrite(HAS_DCF77, LOW);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case 1: // 100ms after start of second -> end of timeframe for logic 0
|
|
||||||
if (DCF_Frame[bit] == dcf_zero)
|
|
||||||
digitalWrite(HAS_DCF77, HIGH);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case 2: // 200ms after start of second -> end of timeframe for logic signal
|
|
||||||
digitalWrite(HAS_DCF77, HIGH);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case 9: // last pulse before next second starts
|
|
||||||
pulse = 0;
|
|
||||||
if (bit++ != DCF77_FRAME_SIZE)
|
|
||||||
return;
|
|
||||||
else { // last pulse of DCF77 frame (59th second)
|
|
||||||
bit = 0;
|
|
||||||
BitsPending = false;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
|
|
||||||
}; // switch
|
|
||||||
}; // while
|
|
||||||
|
|
||||||
// get next frame to send from queue
|
|
||||||
if (xQueueReceive(DCFSendQueue, &DCF_Frame, (TickType_t)0) == pdTRUE)
|
|
||||||
BitsPending = true;
|
|
||||||
|
|
||||||
} // DCF_Ticker()
|
|
||||||
|
|
||||||
void dcf77_loop(void *pvParameters) {
|
|
||||||
|
|
||||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
|
||||||
|
|
||||||
// task remains in blocked state until it is notified by isr
|
|
||||||
for (;;) {
|
|
||||||
xTaskNotifyWait(
|
|
||||||
0x00, // don't clear any bits on entry
|
|
||||||
ULONG_MAX, // clear all bits on exit
|
|
||||||
NULL,
|
|
||||||
portMAX_DELAY); // wait forever (missing error handling here...)
|
|
||||||
|
|
||||||
DCF_Ticker();
|
|
||||||
}
|
|
||||||
vTaskDelete(DCF77Task); // shoud never be reached
|
|
||||||
} // dcf77_loop()
|
|
||||||
|
|
||||||
uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos,
|
uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos,
|
||||||
uint8_t pArray[]) {
|
uint8_t pArray[]) {
|
||||||
|
|
||||||
@ -123,7 +65,7 @@ uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos,
|
|||||||
return parity;
|
return parity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void enqueueTimeframe(time_t t) {
|
void generateTimeframe(time_t t) {
|
||||||
|
|
||||||
uint8_t ParityCount;
|
uint8_t ParityCount;
|
||||||
|
|
||||||
@ -156,21 +98,7 @@ void enqueueTimeframe(time_t t) {
|
|||||||
|
|
||||||
// ENCODE TAIL (bit 59)
|
// ENCODE TAIL (bit 59)
|
||||||
DCFtimeframe[59] = dcf_off;
|
DCFtimeframe[59] = dcf_off;
|
||||||
// --> missing code here for switching second!
|
// !! missing code here for leap second !!
|
||||||
/*
|
|
||||||
In unregelmäßigen Zeitabständen muss eine Schaltsekunde eingefügt werden. Dies
|
|
||||||
ist dadurch bedingt, dass sich die Erde nicht genau in 24 Stunden um sich
|
|
||||||
selbst dreht. Auf die koordinierte Weltzeitskala UTC bezogen, wird diese
|
|
||||||
Korrektur zum Ende der letzten Stunde des 31. Dezember oder 30. Juni
|
|
||||||
vorgenommen. In Mitteleuropa muss die Schaltsekunde daher am 1. Januar um 1.00
|
|
||||||
Uhr MEZ oder am 1.Juli um 2.00 MESZ eingeschoben werden. Zu den genannten
|
|
||||||
Zeiten werden daher 61 Sekunden gesendet.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// post generated DCFtimeframe data to DCF SendQueue
|
|
||||||
if (xQueueSendToBack(DCFSendQueue, (void *)&DCFtimeframe[0], (TickType_t)0) !=
|
|
||||||
pdPASS)
|
|
||||||
ESP_LOGE(TAG, "Failed to send DCF data");
|
|
||||||
|
|
||||||
// for debug: print the DCF77 frame buffer
|
// for debug: print the DCF77 frame buffer
|
||||||
char out[DCF77_FRAME_SIZE + 1];
|
char out[DCF77_FRAME_SIZE + 1];
|
||||||
@ -179,41 +107,80 @@ void enqueueTimeframe(time_t t) {
|
|||||||
out[i] = DCFtimeframe[i] + '0'; // convert int digit to printable ascii
|
out[i] = DCFtimeframe[i] + '0'; // convert int digit to printable ascii
|
||||||
}
|
}
|
||||||
out[DCF77_FRAME_SIZE] = '\0'; // string termination char
|
out[DCF77_FRAME_SIZE] = '\0'; // string termination char
|
||||||
ESP_LOGD(TAG, "DCF=%s", out);
|
ESP_LOGD(TAG, "DCF Timeframe = %s", out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendDCF77() {
|
// called every 100msec by hardware time
|
||||||
|
void DCF_Out() {
|
||||||
|
|
||||||
time_t t = now();
|
static uint8_t bit = 0;
|
||||||
|
static uint8_t pulse = 0;
|
||||||
|
|
||||||
/*
|
if (!BitsPending) {
|
||||||
if (second(t) > 56) {
|
// prepare next frame to send
|
||||||
delay(30000);
|
generateTimeframe(now());
|
||||||
return;
|
BitsPending = true;
|
||||||
|
// wait until next minute, then kick off hardware timer and first DCF pulse
|
||||||
|
do {
|
||||||
|
delay(2);
|
||||||
|
} while (second());
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// enqueue DCF timeframes for each i minute
|
// ticker out current frame
|
||||||
for (uint8_t i = 0; i < DCF_FRAME_QUEUE_SIZE; i++)
|
while (BitsPending) {
|
||||||
enqueueTimeframe(t + i * 60);
|
switch (pulse++) {
|
||||||
|
|
||||||
/*
|
case 0: // start of second -> start of timeframe for logic signal
|
||||||
// how many to the minute end ?
|
if (DCFtimeframe[bit] != dcf_off)
|
||||||
// don't forget that we begin transmission at second 58
|
digitalWrite(HAS_DCF77, LOW);
|
||||||
delay((58 - second(t)) * 1000);
|
return;
|
||||||
|
|
||||||
// three minutes are needed to transmit all the packet
|
case 1: // 100ms after start of second -> end of timeframe for logic 0
|
||||||
// then wait more 30 secs to locate safely at the half of minute
|
if (DCFtimeframe[bit] == dcf_zero)
|
||||||
// NB 150+60=210sec, 60secs are lost from main routine
|
digitalWrite(HAS_DCF77, HIGH);
|
||||||
delay(150000);
|
return;
|
||||||
*/
|
|
||||||
|
|
||||||
} // Ende ReadAndDecodeTime()
|
case 2: // 200ms after start of second -> end of timeframe for logic 1
|
||||||
|
digitalWrite(HAS_DCF77, HIGH);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 9: // last pulse before next second starts
|
||||||
|
pulse = 0;
|
||||||
|
if (bit++ != DCF77_FRAME_SIZE)
|
||||||
|
return;
|
||||||
|
else { // end of DCF77 frame (59th second)
|
||||||
|
bit = 0;
|
||||||
|
BitsPending = false;
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
}; // switch
|
||||||
|
}; // while
|
||||||
|
|
||||||
|
} // DCF_Out()
|
||||||
|
|
||||||
// interrupt service routine triggered each 100ms by ESP32 hardware timer
|
// interrupt service routine triggered each 100ms by ESP32 hardware timer
|
||||||
void IRAM_ATTR DCF77IRQ() {
|
void IRAM_ATTR DCF77IRQ() {
|
||||||
xTaskNotifyFromISR(DCF77Task, xTaskGetTickCountFromISR(), eSetBits, NULL);
|
xTaskNotifyFromISR(DCF77Task, 0, eNoAction, NULL);
|
||||||
portYIELD_FROM_ISR();
|
portYIELD_FROM_ISR();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dcf77_loop(void *pvParameters) {
|
||||||
|
|
||||||
|
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
||||||
|
|
||||||
|
// task remains in blocked state until it is notified by isr
|
||||||
|
for (;;) {
|
||||||
|
xTaskNotifyWait(
|
||||||
|
0x00, // don't clear any bits on entry
|
||||||
|
ULONG_MAX, // clear all bits on exit
|
||||||
|
NULL,
|
||||||
|
portMAX_DELAY); // wait forever (missing error handling here...)
|
||||||
|
|
||||||
|
DCF_Out();
|
||||||
|
}
|
||||||
|
BitsPending = false; // stop blink in display
|
||||||
|
vTaskDelete(DCF77Task); // shoud never be reached
|
||||||
|
} // dcf77_loop()
|
||||||
|
|
||||||
#endif // HAS_DCF77
|
#endif // HAS_DCF77
|
@ -145,14 +145,14 @@ void refreshtheDisplay() {
|
|||||||
|
|
||||||
uint8_t msgWaiting;
|
uint8_t msgWaiting;
|
||||||
char buff[16]; // 16 chars line buffer
|
char buff[16]; // 16 chars line buffer
|
||||||
#if defined HAS_RTC || defined HAS_GPS
|
#if (defined HAS_DCF77) || (defined HAS_IF482)
|
||||||
const char timeNosyncSymbol = '?';
|
const char timeNosyncSymbol = '?';
|
||||||
#if defined HAS_IF482 || defined HAS_DCF77
|
#if (defined HAS_IF482)
|
||||||
const char timesyncSymbol = '+';
|
const char timesyncSymbol = '+';
|
||||||
#else
|
#else
|
||||||
const char timesyncSymbol = '*';
|
const char timesyncSymbol = '*';
|
||||||
#endif
|
#endif
|
||||||
#endif // HAS_RTC
|
#endif
|
||||||
|
|
||||||
// update counter (lines 0-1)
|
// update counter (lines 0-1)
|
||||||
snprintf(
|
snprintf(
|
||||||
@ -217,21 +217,20 @@ void refreshtheDisplay() {
|
|||||||
|
|
||||||
#ifdef HAS_LORA
|
#ifdef HAS_LORA
|
||||||
u8x8.setCursor(0, 6);
|
u8x8.setCursor(0, 6);
|
||||||
#if (!defined HAS_RTC) && (!defined HAS_GPS)
|
#if (!defined HAS_DCF77) && (!defined HAS_IF482)
|
||||||
// update LoRa status display (line 6)
|
// update LoRa status display (line 6)
|
||||||
u8x8.printf("%-16s", display_line6);
|
u8x8.printf("%-16s", display_line6);
|
||||||
#else // HAS_RTC or HAS_GPS
|
#else // we want a time display instead LoRa status
|
||||||
// update time/date display (line 6)
|
// update time/date display (line 6)
|
||||||
time_t t = myTZ.toLocal(now());
|
time_t t = myTZ.toLocal(now());
|
||||||
char timeState =
|
char timeState =
|
||||||
timeStatus() == timeSet ? timesyncSymbol : timeNosyncSymbol;
|
timeStatus() == timeSet ? timesyncSymbol : timeNosyncSymbol;
|
||||||
#ifdef RTC_INT // make timestatus symbol blinking if pps line
|
// make timestatus symbol blinking if pps line
|
||||||
if (second(t) % 2)
|
if ((BitsPending) && (second(t) % 2))
|
||||||
timeState = ' ';
|
timeState = ' ';
|
||||||
#endif // RTC_INT
|
|
||||||
u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t),
|
u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t),
|
||||||
timeState, day(t), printmonth[month(t)]);
|
timeState, day(t), printmonth[month(t)]);
|
||||||
#endif // HAS_RTC
|
#endif
|
||||||
|
|
||||||
// update LMiC event display (line 7)
|
// update LMiC event display (line 7)
|
||||||
u8x8.setCursor(0, 7);
|
u8x8.setCursor(0, 7);
|
||||||
|
@ -103,7 +103,20 @@ int if482_init(void) {
|
|||||||
ESP_LOGE(TAG, "I2c bus busy - IF482 initialization error");
|
ESP_LOGE(TAG, "I2c bus busy - IF482 initialization error");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xTaskCreatePinnedToCore(if482_loop, // task function
|
||||||
|
"if482loop", // name of task
|
||||||
|
2048, // stack size of task
|
||||||
|
(void *)1, // parameter of the task
|
||||||
|
3, // priority of the task
|
||||||
|
&IF482Task, // task handle
|
||||||
|
0); // CPU core
|
||||||
|
|
||||||
|
assert(IF482Task); // has if482loop task started?
|
||||||
|
// setup external interupt for active low RTC INT pin
|
||||||
pinMode(RTC_INT, INPUT_PULLUP);
|
pinMode(RTC_INT, INPUT_PULLUP);
|
||||||
|
attachInterrupt(digitalPinToInterrupt(RTC_INT), IF482IRQ, FALLING);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
} // if482_init
|
} // if482_init
|
||||||
@ -134,6 +147,8 @@ String if482Telegram(time_t tt) {
|
|||||||
month(t), day(t), weekday(t), hour(t), minute(t), second(t));
|
month(t), day(t), weekday(t), hour(t), minute(t), second(t));
|
||||||
|
|
||||||
snprintf(out, sizeof out, "O%cL%s\r", mon, buf);
|
snprintf(out, sizeof out, "O%cL%s\r", mon, buf);
|
||||||
|
ESP_LOGD(TAG, "IF482 = %s", out);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,6 +168,8 @@ void if482_loop(void *pvParameters) {
|
|||||||
tt = now();
|
tt = now();
|
||||||
} while (t == tt);
|
} while (t == tt);
|
||||||
|
|
||||||
|
BitsPending = true; // start blink in display
|
||||||
|
|
||||||
// take timestamp at moment of start of new second
|
// take timestamp at moment of start of new second
|
||||||
const TickType_t shotTime = xTaskGetTickCount() - startTime - timeOffset;
|
const TickType_t shotTime = xTaskGetTickCount() - startTime - timeOffset;
|
||||||
|
|
||||||
@ -169,6 +186,7 @@ void if482_loop(void *pvParameters) {
|
|||||||
vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot
|
vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot
|
||||||
IF482.print(if482Telegram(now() + 1));
|
IF482.print(if482Telegram(now() + 1));
|
||||||
}
|
}
|
||||||
|
BitsPending = false; // stop blink in display
|
||||||
vTaskDelete(IF482Task); // shoud never be reached
|
vTaskDelete(IF482Task); // shoud never be reached
|
||||||
} // if482_loop()
|
} // if482_loop()
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
// faster or slower. This causes the transceiver to be earlier switched on,
|
// faster or slower. This causes the transceiver to be earlier switched on,
|
||||||
// so consuming more power. You may sharpen (reduce) this value if you are
|
// so consuming more power. You may sharpen (reduce) this value if you are
|
||||||
// limited on battery.
|
// limited on battery.
|
||||||
#define CLOCK_ERROR_PROCENTAGE 30
|
#define CLOCK_ERROR_PROCENTAGE 3
|
||||||
|
|
||||||
// Set this to 1 to enable some basic debug output (using printf) about
|
// Set this to 1 to enable some basic debug output (using printf) about
|
||||||
// RF settings used during transmission and reception. Set to 2 to
|
// RF settings used during transmission and reception. Set to 2 to
|
||||||
|
43
src/main.cpp
43
src/main.cpp
@ -34,7 +34,7 @@ spiloop 0 2 reads/writes data on spi interface
|
|||||||
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
|
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
|
||||||
|
|
||||||
looptask 1 1 arduino core -> runs the LMIC LoRa stack
|
looptask 1 1 arduino core -> runs the LMIC LoRa stack
|
||||||
irqhandler 1 1 executes tasks triggered by irq
|
irqhandler 1 1 executes tasks triggered by hw irq, see table below
|
||||||
gpsloop 1 2 reads data from GPS via serial or i2c
|
gpsloop 1 2 reads data from GPS via serial or i2c
|
||||||
bmeloop 1 1 reads data from BME sensor via i2c
|
bmeloop 1 1 reads data from BME sensor via i2c
|
||||||
IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
|
IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
|
||||||
@ -44,7 +44,7 @@ Low priority numbers denote low priority tasks.
|
|||||||
Tasks using i2c bus all must have same priority, because using mutex semaphore
|
Tasks using i2c bus all must have same priority, because using mutex semaphore
|
||||||
(irqhandler, bmeloop)
|
(irqhandler, bmeloop)
|
||||||
|
|
||||||
ESP32 hardware timers
|
ESP32 hardware irq timers
|
||||||
================================
|
================================
|
||||||
0 triggers display refresh
|
0 triggers display refresh
|
||||||
1 triggers DCF77 clock signal
|
1 triggers DCF77 clock signal
|
||||||
@ -65,6 +65,7 @@ char display_line6[16], display_line7[16]; // display buffers
|
|||||||
uint8_t volatile channel = 0; // channel rotation counter
|
uint8_t volatile channel = 0; // channel rotation counter
|
||||||
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
|
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
|
||||||
batt_voltage = 0; // globals for display
|
batt_voltage = 0; // globals for display
|
||||||
|
bool volatile BitsPending = false; // DCF77 or IF482 ticker indicator
|
||||||
|
|
||||||
hw_timer_t *sendCycle = NULL, *homeCycle = NULL;
|
hw_timer_t *sendCycle = NULL, *homeCycle = NULL;
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
@ -97,7 +98,7 @@ void setup() {
|
|||||||
char features[100] = "";
|
char features[100] = "";
|
||||||
|
|
||||||
I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus
|
I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus
|
||||||
if ((I2Caccess) != NULL)
|
if (I2Caccess)
|
||||||
xSemaphoreGive((I2Caccess)); // Flag the i2c bus available for use
|
xSemaphoreGive((I2Caccess)); // Flag the i2c bus available for use
|
||||||
|
|
||||||
// disable brownout detection
|
// disable brownout detection
|
||||||
@ -334,7 +335,6 @@ void setup() {
|
|||||||
|
|
||||||
#if defined HAS_DCF77
|
#if defined HAS_DCF77
|
||||||
strcat_P(features, " DCF77");
|
strcat_P(features, " DCF77");
|
||||||
assert(dcf77_init());
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined HAS_IF482 && defined RTC_INT
|
#if defined HAS_IF482 && defined RTC_INT
|
||||||
@ -417,37 +417,14 @@ void setup() {
|
|||||||
setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60);
|
setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined HAS_IF482 && defined RTC_INT
|
#if defined HAS_IF482 && defined DCF_77
|
||||||
|
#error "You may define at most one of HAS_IF482 or DCF_77"
|
||||||
|
#elif defined HAS_IF482 && defined RTC_INT
|
||||||
ESP_LOGI(TAG, "Starting IF482 Generator...");
|
ESP_LOGI(TAG, "Starting IF482 Generator...");
|
||||||
xTaskCreatePinnedToCore(if482_loop, // task function
|
assert(if482_init());
|
||||||
"if482loop", // name of task
|
#elif defined HAS_DCF77
|
||||||
2048, // stack size of task
|
|
||||||
(void *)1, // parameter of the task
|
|
||||||
3, // priority of the task
|
|
||||||
&IF482Task, // task handle
|
|
||||||
0); // CPU core
|
|
||||||
|
|
||||||
// setup external interupt for active low RTC INT pin
|
|
||||||
assert(IF482Task != NULL); // has if482loop task started?
|
|
||||||
attachInterrupt(digitalPinToInterrupt(RTC_INT), IF482IRQ, FALLING);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined HAS_DCF77
|
|
||||||
ESP_LOGI(TAG, "Starting DCF77 Generator...");
|
ESP_LOGI(TAG, "Starting DCF77 Generator...");
|
||||||
xTaskCreatePinnedToCore(dcf77_loop, // task function
|
assert(dcf77_init());
|
||||||
"dcf77loop", // name of task
|
|
||||||
2048, // stack size of task
|
|
||||||
(void *)1, // parameter of the task
|
|
||||||
3, // priority of the task
|
|
||||||
&DCF77Task, // task handle
|
|
||||||
0); // CPU core
|
|
||||||
|
|
||||||
// setup 100ms clock signal for DCF77 generator using esp32 hardware timer 1
|
|
||||||
assert(DCF77Task != NULL); // has dcf77 task started?
|
|
||||||
dcfCycle = timerBegin(1, 8000, true);
|
|
||||||
timerAttachInterrupt(dcfCycle, &DCF77IRQ, true);
|
|
||||||
timerAlarmWrite(dcfCycle, 1000, true);
|
|
||||||
timerAlarmEnable(dcfCycle);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // setup()
|
} // setup()
|
||||||
|
Loading…
Reference in New Issue
Block a user