From cd083356349aa06a547461549cdf3596d6007f57 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 2 Feb 2019 21:35:40 +0100 Subject: [PATCH 01/23] free up hw timer #1 (for future DCF77 use) --- include/globals.h | 5 +++-- include/irqhandler.h | 1 - include/wifiscan.h | 2 +- src/cyclic.cpp | 5 +---- src/irqhandler.cpp | 5 ----- src/main.cpp | 25 ++++--------------------- src/rcommand.cpp | 4 ++-- src/wifiscan.cpp | 36 ++++++++++++++++++++---------------- 8 files changed, 31 insertions(+), 52 deletions(-) diff --git a/include/globals.h b/include/globals.h index 6aaad2c7..1e8f43d0 100644 --- a/include/globals.h +++ b/include/globals.h @@ -105,8 +105,9 @@ extern std::set, Mallocator> macs; extern std::array::iterator it; extern std::array beacons; -extern TaskHandle_t irqHandlerTask, wifiSwitchTask; -extern Timezone myTZ; // make Timezone myTZ globally available +extern TaskHandle_t irqHandlerTask; +extern TimerHandle_t WifiChanTimer; +extern Timezone myTZ; // application includes #include "led.h" diff --git a/include/irqhandler.h b/include/irqhandler.h index 45d58fee..cc8d15ac 100644 --- a/include/irqhandler.h +++ b/include/irqhandler.h @@ -11,7 +11,6 @@ #include "senddata.h" void irqHandler(void *pvParameters); -void IRAM_ATTR ChannelSwitchIRQ(); void IRAM_ATTR homeCycleIRQ(); void IRAM_ATTR SendCycleIRQ(); diff --git a/include/wifiscan.h b/include/wifiscan.h index 0e6962a2..0a6e7b6c 100644 --- a/include/wifiscan.h +++ b/include/wifiscan.h @@ -9,6 +9,6 @@ void wifi_sniffer_init(void); void IRAM_ATTR wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type); -void switchWifiChannel(void * parameter); +void switchWifiChannel(TimerHandle_t xTimer); #endif \ No newline at end of file diff --git a/src/cyclic.cpp b/src/cyclic.cpp index a26bf6f8..93c200d7 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -41,7 +41,7 @@ void doHousekeeping() { if ((millis() >= nextRTCTimeSync) && (timeStatus() == timeSet)) { nextRTCTimeSync = millis() + TIME_WRITE_INTERVAL_RTC * 60000; // set up next time sync period - if (!set_rtctime(now())) // epoch time + if (!set_rtctime(now())) // epoch time ESP_LOGE(TAG, "RTC set time failure"); else ESP_LOGI(TAG, "RTC time updated"); @@ -49,9 +49,6 @@ void doHousekeeping() { #endif // task storage debugging // - ESP_LOGD(TAG, "Wifiloop %d bytes left | Taskstate = %d", - uxTaskGetStackHighWaterMark(wifiSwitchTask), - eTaskGetState(wifiSwitchTask)); ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d", uxTaskGetStackHighWaterMark(irqHandlerTask), eTaskGetState(irqHandlerTask)); diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp index f79e035e..fbbf06fa 100644 --- a/src/irqhandler.cpp +++ b/src/irqhandler.cpp @@ -44,11 +44,6 @@ void irqHandler(void *pvParameters) { // esp32 hardware timer triggered interrupt service routines // they notify the irq handler task -void IRAM_ATTR ChannelSwitchIRQ() { - xTaskNotifyGive(wifiSwitchTask); - portYIELD_FROM_ISR(); -} - void IRAM_ATTR homeCycleIRQ() { xTaskNotifyFromISR(irqHandlerTask, CYCLIC_IRQ, eSetBits, NULL); portYIELD_FROM_ISR(); diff --git a/src/main.cpp b/src/main.cpp index 5734f97f..2a029a1f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,7 +27,6 @@ Uused tasks and timers: Task Core Prio Purpose ==================================================================================== -wifiloop 0 4 rotates wifi channels ledloop 0 3 blinks LEDs if482loop 1 3 serial feed of IF482 time telegrams spiloop 0 2 reads/writes data on spi interface @@ -37,7 +36,7 @@ looptask 1 1 arduino core -> runs the LMIC LoRa stack irqhandler 1 1 executes tasks triggered by irq gpsloop 1 2 reads data from GPS via serial or i2c bmeloop 1 1 reads data from BME sensor via i2c -IDLE 1 0 ESP32 arduino scheduler +IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator Low priority numbers denote low priority tasks. @@ -47,7 +46,7 @@ Tasks using i2c bus all must have same priority, because using mutex semaphore ESP32 hardware timers ================================ 0 triggers display refresh - 1 triggers Wifi channel switch + 1 unused (reserved for DCF77) 2 triggers send payload cycle 3 triggers housekeeping cycle @@ -67,7 +66,7 @@ uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, batt_voltage = 0; // globals for display hw_timer_t *channelSwitch = NULL, *sendCycle = NULL, *homeCycle = NULL, *displaytimer = NULL; // irq tasks -TaskHandle_t irqHandlerTask, wifiSwitchTask; +TaskHandle_t irqHandlerTask; SemaphoreHandle_t I2Caccess; // container holding unique MAC address hashes with Memory Alloctor using PSRAM, @@ -305,11 +304,6 @@ void setup() { timerAttachInterrupt(homeCycle, &homeCycleIRQ, true); timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true); - // setup channel rotation trigger IRQ using esp32 hardware timer 1 - channelSwitch = timerBegin(1, 800, true); - timerAttachInterrupt(channelSwitch, &ChannelSwitchIRQ, true); - timerAlarmWrite(channelSwitch, cfg.wifichancycle * 1000, true); - // show payload encoder #if PAYLOAD_ENCODER == 1 strcat_P(features, " PLAIN"); @@ -343,7 +337,7 @@ void setup() { #endif #endif - // start wifi in monitor mode and start channel rotation task on core 0 + // start wifi in monitor mode and start channel rotation timer ESP_LOGI(TAG, "Starting Wifi..."); wifi_sniffer_init(); // initialize salt value using esp_random() called by random() in @@ -361,16 +355,6 @@ void setup() { &irqHandlerTask, // task handle 1); // CPU core - // start wifi channel rotation task - ESP_LOGI(TAG, "Starting Wifi Channel rotation..."); - xTaskCreatePinnedToCore(switchWifiChannel, // task function - "wifiloop", // name of task - 2048, // stack size of task - NULL, // parameter of the task - 4, // priority of the task - &wifiSwitchTask, // task handle - 0); // CPU core - // initialize bme #ifdef HAS_BME strcat_P(features, " BME"); @@ -394,7 +378,6 @@ void setup() { #endif timerAlarmEnable(sendCycle); timerAlarmEnable(homeCycle); - timerAlarmEnable(channelSwitch); // start button interrupt #ifdef HAS_BUTTON diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 3b6bccc6..90a2e20f 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -67,8 +67,8 @@ void set_sendcycle(uint8_t val[]) { void set_wifichancycle(uint8_t val[]) { cfg.wifichancycle = val[0]; - // update channel rotation interrupt - timerAlarmWrite(channelSwitch, cfg.wifichancycle * 10000, true); + // update Wifi channel rotation timer period + xTimerChangePeriod(WifiChanTimer, pdMS_TO_TICKS(cfg.wifichancycle * 10), 100 ); ESP_LOGI(TAG, "Remote command: set Wifi channel switch interval to %.1f seconds", diff --git a/src/wifiscan.cpp b/src/wifiscan.cpp index dcd8ebc9..a6130ad9 100644 --- a/src/wifiscan.cpp +++ b/src/wifiscan.cpp @@ -7,6 +7,8 @@ // Local logging tag static const char TAG[] = "wifi"; +TimerHandle_t WifiChanTimer; + static wifi_country_t wifi_country = {WIFI_MY_COUNTRY, WIFI_CHANNEL_MIN, WIFI_CHANNEL_MAX, 100, WIFI_COUNTRY_POLICY_MANUAL}; @@ -43,10 +45,17 @@ IRAM_ATTR void wifi_sniffer_packet_handler(void *buff, mac_add((uint8_t *)hdr->addr2, ppkt->rx_ctrl.rssi, MAC_SNIFF_WIFI); } +// Software-timer driven Wifi channel rotation callback function +void switchWifiChannel(TimerHandle_t xTimer) { + channel = + (channel % WIFI_CHANNEL_MAX) + 1; // rotate channel 1..WIFI_CHANNEL_MAX + esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); + } + void wifi_sniffer_init(void) { - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - cfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM - cfg.wifi_task_core_id = 0; // we want wifi task running on core 0 + wifi_init_config_t wificfg = WIFI_INIT_CONFIG_DEFAULT(); + wificfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM + wificfg.wifi_task_core_id = 0; // we want wifi task running on core 0 wifi_promiscuous_filter_t filter = { // .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // only MGMT frames .filter_mask = WIFI_PROMIS_FILTER_MASK_ALL}; // we use all frames @@ -54,7 +63,7 @@ void wifi_sniffer_init(void) { ESP_ERROR_CHECK(esp_coex_preference_set( ESP_COEX_PREFER_BALANCE)); // configure Wifi/BT coexist lib - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // configure Wifi with cfg + ESP_ERROR_CHECK(esp_wifi_init(&wificfg)); // configure Wifi with cfg ESP_ERROR_CHECK( esp_wifi_set_country(&wifi_country)); // set locales for RF and channels ESP_ERROR_CHECK( @@ -65,16 +74,11 @@ void wifi_sniffer_init(void) { esp_wifi_set_promiscuous_filter(&filter)); // set MAC frame filter ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler)); ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode -} -// Wifi channel rotation task -void switchWifiChannel(void *parameter) { - while (1) { - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // waiting for channel switch timer - channel = - (channel % WIFI_CHANNEL_MAX) + 1; // rotate channel 1..WIFI_CHANNEL_MAX - esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); - //ESP_LOGD(TAG, "Wifi set channel %d", channel); - } - vTaskDelete(NULL); // shoud never be reached -} + // setup wifi channel rotation timer + WifiChanTimer = + xTimerCreate("WifiChannelTimer", pdMS_TO_TICKS(cfg.wifichancycle * 10), + pdTRUE, (void *)0, switchWifiChannel); + assert(WifiChanTimer); + xTimerStart(WifiChanTimer, 0); +} \ No newline at end of file From 06a17757793b54525031b8bc952b564d658d5c2f Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 3 Feb 2019 14:12:21 +0100 Subject: [PATCH 02/23] added priority control for lora and spi sendqueue --- include/globals.h | 8 +++++++- include/lorawan.h | 2 +- include/senddata.h | 2 +- include/spislave.h | 2 +- src/button.cpp | 2 +- src/cyclic.cpp | 4 ++-- src/lorawan.cpp | 16 +++++++++++++--- src/macsniff.cpp | 2 +- src/paxcounter.conf | 2 +- src/rcommand.cpp | 8 ++++---- src/senddata.cpp | 20 ++++++++++---------- src/spislave.cpp | 23 +++++++++++++++++------ 12 files changed, 59 insertions(+), 32 deletions(-) diff --git a/include/globals.h b/include/globals.h index 1e8f43d0..73c11287 100644 --- a/include/globals.h +++ b/include/globals.h @@ -92,13 +92,15 @@ typedef struct { float gas; // raw gas sensor signal } bmeStatus_t; +enum sendprio_t { prio_low, prio_normal, prio_high }; + // global variables extern configData_t cfg; // current device configuration extern char display_line6[], display_line7[]; // screen buffers extern uint8_t volatile channel; // wifi channel rotation counter extern uint16_t volatile macs_total, macs_wifi, macs_ble, batt_voltage; // display values -extern hw_timer_t *channelSwitch, *sendCycle, *displaytimer; +extern hw_timer_t *sendCycle, *displaytimer; extern SemaphoreHandle_t I2Caccess; extern std::set, Mallocator> macs; @@ -150,4 +152,8 @@ extern Timezone myTZ; #include "if482.h" #endif +#ifdef HAS_DCF77 +#include "dcf77.h" +#endif + #endif \ No newline at end of file diff --git a/include/lorawan.h b/include/lorawan.h index 05aba7cd..4beee6c4 100644 --- a/include/lorawan.h +++ b/include/lorawan.h @@ -33,7 +33,7 @@ void os_getDevEui(u1_t *buf); void showLoraKeys(void); void switch_lora(uint8_t sf, uint8_t tx); void lora_send(osjob_t *job); -void lora_enqueuedata(MessageBuffer_t *message); +void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio); void lora_queuereset(void); void lora_housekeeping(void); void user_request_network_time_callback(void *pVoidUserUTCTime, diff --git a/include/senddata.h b/include/senddata.h index da368f71..82cf4581 100644 --- a/include/senddata.h +++ b/include/senddata.h @@ -5,7 +5,7 @@ #include "lorawan.h" #include "cyclic.h" -void SendPayload(uint8_t port); +void SendPayload(uint8_t port, sendprio_t prio); void sendCounter(void); void checkSendQueues(void); void flushQueues(); diff --git a/include/spislave.h b/include/spislave.h index f459b2fd..74b77db0 100644 --- a/include/spislave.h +++ b/include/spislave.h @@ -28,7 +28,7 @@ licenses. Refer to LICENSE.txt file in repository for more details. esp_err_t spi_init(); -void spi_enqueuedata(MessageBuffer_t *message); +void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio); void spi_queuereset(); void spi_housekeeping(); diff --git a/src/button.cpp b/src/button.cpp index 29eff252..d7d5c2a4 100644 --- a/src/button.cpp +++ b/src/button.cpp @@ -10,6 +10,6 @@ void readButton() { ESP_LOGI(TAG, "Button pressed"); payload.reset(); payload.addButton(0x01); - SendPayload(BUTTONPORT); + SendPayload(BUTTONPORT, prio_normal); } #endif \ No newline at end of file diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 93c200d7..67fcc99c 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -85,7 +85,7 @@ void doHousekeeping() { "Memory full, counter cleared (heap low water mark = %d Bytes / " "free heap = %d bytes)", ESP.getMinFreeHeap(), ESP.getFreeHeap()); - SendPayload(COUNTERPORT); // send data before clearing counters + SendPayload(COUNTERPORT, prio_high); // send data before clearing counters reset_counters(); // clear macs container and reset all counters get_salt(); // get new salt for salting hashes @@ -97,7 +97,7 @@ void doHousekeeping() { #ifdef BOARD_HAS_PSRAM if (ESP.getMinFreePsram() <= MEM_LOW) { ESP_LOGI(TAG, "PSRAM full, counter cleared"); - SendPayload(COUNTERPORT); // send data before clearing counters + SendPayload(COUNTERPORT, prio_high); // send data before clearing counters reset_counters(); // clear macs container and reset all counters get_salt(); // get new salt for salting hashes diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 77c08c76..bc8565de 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -358,6 +358,7 @@ esp_err_t lora_stack_init() { #ifndef HAS_LORA return ESP_OK; // continue main program #else + assert(SEND_QUEUE_SIZE); LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); if (LoraSendQueue == 0) { ESP_LOGE(TAG, "Could not create LORA send queue. Aborting."); @@ -397,11 +398,20 @@ esp_err_t lora_stack_init() { #endif } -void lora_enqueuedata(MessageBuffer_t *message) { +void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { // enqueue message in LORA send queue #ifdef HAS_LORA - BaseType_t ret = - xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0); + BaseType_t ret; + switch (prio) { + case prio_high: + ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0); + break; + case prio_low: + case prio_normal: + default: + ret = xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0); + break; + } if (ret == pdTRUE) { ESP_LOGI(TAG, "%d bytes enqueued for LORA interface", message->MessageSize); } else { diff --git a/src/macsniff.cpp b/src/macsniff.cpp index b1e5ffcc..5cc02c64 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -107,7 +107,7 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) { #endif payload.reset(); payload.addAlarm(rssi, beaconID); - SendPayload(BEACONPORT); + SendPayload(BEACONPORT, prio_high); } }; diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 1c3c5618..9ca0766e 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -47,7 +47,7 @@ #define PAYLOAD_BUFFER_SIZE 51 // maximum size of payload block per transmit #define LORASFDEFAULT 9 // 7 ... 12 SF, according to LoRaWAN specs #define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy -#define SEND_QUEUE_SIZE 10 // maximum number of messages in payload send queue +#define SEND_QUEUE_SIZE 10 // maximum number of messages in payload send queue [1 = no queue] // Ports on which the device sends and listenes on LoRaWAN and SPI #define COUNTERPORT 1 // Port on which device sends counts diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 90a2e20f..038db7db 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -233,7 +233,7 @@ void get_config(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: get device configuration"); payload.reset(); payload.addConfig(cfg); - SendPayload(CONFIGPORT); + SendPayload(CONFIGPORT, prio_high); }; void get_status(uint8_t val[]) { @@ -247,7 +247,7 @@ void get_status(uint8_t val[]) { payload.addStatus(voltage, uptime() / 1000, temperatureRead(), getFreeRAM(), rtc_get_reset_reason(0), rtc_get_reset_reason(1)); - SendPayload(STATUSPORT); + SendPayload(STATUSPORT, prio_high); }; void get_gps(uint8_t val[]) { @@ -256,7 +256,7 @@ void get_gps(uint8_t val[]) { gps_read(); payload.reset(); payload.addGPS(gps_status); - SendPayload(GPSPORT); + SendPayload(GPSPORT, prio_high); #else ESP_LOGW(TAG, "GPS function not supported"); #endif @@ -267,7 +267,7 @@ void get_bme(uint8_t val[]) { #ifdef HAS_BME payload.reset(); payload.addBME(bme_status); - SendPayload(BMEPORT); + SendPayload(BMEPORT, prio_high); #else ESP_LOGW(TAG, "BME680 sensor not supported"); #endif diff --git a/src/senddata.cpp b/src/senddata.cpp index fe03be25..c13d907f 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -2,7 +2,7 @@ #include "senddata.h" // put data to send in RTos Queues used for transmit over channels Lora and SPI -void SendPayload(uint8_t port) { +void SendPayload(uint8_t port, sendprio_t prio) { MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, Message[] @@ -24,8 +24,8 @@ void SendPayload(uint8_t port) { memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize()); // enqueue message in device's send queues - lora_enqueuedata(&SendBuffer); - spi_enqueuedata(&SendBuffer); + lora_enqueuedata(&SendBuffer, prio); + spi_enqueuedata(&SendBuffer, prio); } // SendPayload @@ -55,7 +55,7 @@ void sendCounter() { } #endif - SendPayload(COUNTERPORT); + SendPayload(COUNTERPORT, prio_normal); // clear counter if not in cumulative counter mode if (cfg.countermode != 1) { reset_counters(); // clear macs container and reset all counters @@ -68,7 +68,7 @@ void sendCounter() { case MEMS_DATA: payload.reset(); payload.addBME(bme_status); - SendPayload(BMEPORT); + SendPayload(BMEPORT, prio_normal); break; #endif @@ -79,7 +79,7 @@ void sendCounter() { gps_read(); payload.reset(); payload.addGPS(gps_status); - SendPayload(GPSPORT); + SendPayload(GPSPORT, prio_high); } else ESP_LOGD(TAG, "No valid GPS position"); break; @@ -89,17 +89,17 @@ void sendCounter() { case SENSOR1_DATA: payload.reset(); payload.addSensor(sensor_read(1)); - SendPayload(SENSOR1PORT); + SendPayload(SENSOR1PORT, prio_normal); break; case SENSOR2_DATA: payload.reset(); payload.addSensor(sensor_read(2)); - SendPayload(SENSOR2PORT); + SendPayload(SENSOR2PORT, prio_normal); break; case SENSOR3_DATA: payload.reset(); payload.addSensor(sensor_read(3)); - SendPayload(SENSOR3PORT); + SendPayload(SENSOR3PORT, prio_normal); break; #endif @@ -107,7 +107,7 @@ void sendCounter() { case BATT_DATA: payload.reset(); payload.addVoltage(read_voltage()); - SendPayload(BATTPORT); + SendPayload(BATTPORT, prio_normal); break; #endif diff --git a/src/spislave.cpp b/src/spislave.cpp index 5e9ebb1c..5f6a1b60 100644 --- a/src/spislave.cpp +++ b/src/spislave.cpp @@ -66,7 +66,8 @@ void spi_slave_task(void *param) { uint8_t *messageSize = txbuf + 3; *messageSize = msg.MessageSize; memcpy(txbuf + HEADER_SIZE, &msg.Message, msg.MessageSize); - // calculate crc16 checksum over txbuf and insert checksum at pos 0+1 of txbuf + // calculate crc16 checksum over txbuf and insert checksum at pos 0+1 of + // txbuf uint16_t *crc = (uint16_t *)txbuf; *crc = crc16_be(0, messageType, msg.MessageSize + HEADER_SIZE - 2); @@ -87,7 +88,8 @@ void spi_slave_task(void *param) { // wait until spi master clocks out the data, and read results in rx buffer ESP_LOGI(TAG, "Prepared SPI transaction for %zu byte(s)", transaction_size); ESP_LOG_BUFFER_HEXDUMP(TAG, txbuf, transaction_size, ESP_LOG_DEBUG); - ESP_ERROR_CHECK_WITHOUT_ABORT(spi_slave_transmit(HSPI_HOST, &spi_transaction, portMAX_DELAY)); + ESP_ERROR_CHECK_WITHOUT_ABORT( + spi_slave_transmit(HSPI_HOST, &spi_transaction, portMAX_DELAY)); ESP_LOG_BUFFER_HEXDUMP(TAG, rxbuf, transaction_size, ESP_LOG_DEBUG); ESP_LOGI(TAG, "Transaction finished with size %zu bits", spi_transaction.trans_len); @@ -103,7 +105,7 @@ esp_err_t spi_init() { #ifndef HAS_SPI return ESP_OK; #else - + assert(SEND_QUEUE_SIZE); SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); if (SPISendQueue == 0) { ESP_LOGE(TAG, "Could not create SPI send queue. Aborting."); @@ -148,11 +150,20 @@ esp_err_t spi_init() { #endif } -void spi_enqueuedata(MessageBuffer_t *message) { +void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { // enqueue message in SPI send queue #ifdef HAS_SPI - BaseType_t ret = - xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0); + BaseType_t ret; + switch (prio) { + case prio_high: + ret = xQueueSendToFront(SPISendQueue, (void *)message, (TickType_t)0); + break; + case prio_low: + case prio_normal: + default: + ret = xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0); + break; + } if (ret == pdTRUE) { ESP_LOGI(TAG, "%d byte(s) enqueued for SPI interface", message->MessageSize); From 9cb828bfbdb4fab21bc282f65d2302906fec5905 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 3 Feb 2019 20:18:54 +0100 Subject: [PATCH 03/23] gpsread.cpp: bugfix in debug time display --- src/gpsread.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 27d12ca2..d6c58b99 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -73,9 +73,8 @@ time_t get_gpstime(void) { if ((gps.time.age() < 1500) && (gps.time.isValid())) { t = tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second()); - ESP_LOGD(TAG, "GPS time: %d/%d/%d %d:%d:%d", gps.date.year(), - gps.date.month(), gps.date.day(), gps.time.hour(), - gps.time.minute(), gps.time.second()); + ESP_LOGD(TAG, "GPS time: %d/%d/%d %d:%d:%d", year(t), month(t), day(t), + hour(t), minute(t), second(t)); } else { ESP_LOGW(TAG, "GPS has no confident time"); } From 39e2df7a056adf491e6bd903b4e29c3bb51987c3 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 3 Feb 2019 21:19:08 +0100 Subject: [PATCH 04/23] added DCF77 function (experimental, not fully working yet) --- include/cyclic.h | 5 +- include/dcf77.h | 16 ++++ include/if482.h | 1 - src/cyclic.cpp | 17 +++- src/dcf77.cpp | 219 ++++++++++++++++++++++++++++++++++++++++++++++ src/display.cpp | 11 +-- src/gpsread.cpp | 2 +- src/hal/generic.h | 3 + src/main.cpp | 45 ++++++++-- 9 files changed, 300 insertions(+), 19 deletions(-) create mode 100644 include/dcf77.h create mode 100644 src/dcf77.cpp diff --git a/include/cyclic.h b/include/cyclic.h index 13e9419c..c98ed050 100644 --- a/include/cyclic.h +++ b/include/cyclic.h @@ -11,11 +11,14 @@ #include "bme680mems.h" #endif -// Needed for RTC time sync if RTC present on board #ifdef HAS_RTC #include "rtctime.h" #endif +#ifdef HAS_DCF77 +#include "dcf77.h" +#endif + void doHousekeeping(void); uint64_t uptime(void); void reset_counters(void); diff --git a/include/dcf77.h b/include/dcf77.h new file mode 100644 index 00000000..bf2ffcee --- /dev/null +++ b/include/dcf77.h @@ -0,0 +1,16 @@ +#ifndef _DCF77_H +#define _DCF77_H + +#include "globals.h" + +extern TaskHandle_t DCF77Task; +extern hw_timer_t *dcfCycle; + +enum dcf_pulses { dcf_off, dcf_zero, dcf_one }; + +int dcf77_init(void); +void dcf77_loop(void *pvParameters); +void IRAM_ATTR DCF77IRQ(void); +void sendDCF77(void); + +#endif \ No newline at end of file diff --git a/include/if482.h b/include/if482.h index 7ceca1c5..38a6506b 100644 --- a/include/if482.h +++ b/include/if482.h @@ -2,7 +2,6 @@ #define _IF482_H #include "globals.h" -#include "irqhandler.h" extern TaskHandle_t IF482Task; diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 67fcc99c..5b23817d 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -60,6 +60,10 @@ void doHousekeeping() { ESP_LOGD(TAG, "Bmeloop %d bytes left | Taskstate = %d", uxTaskGetStackHighWaterMark(BmeTask), eTaskGetState(BmeTask)); #endif +#ifdef HAS_DCF77 + ESP_LOGD(TAG, "DCF77loop %d bytes left | Taskstate = %d", + uxTaskGetStackHighWaterMark(DCF77Task), eTaskGetState(DCF77Task)); +#endif #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) ESP_LOGD(TAG, "LEDloop %d bytes left | Taskstate = %d", @@ -79,6 +83,11 @@ void doHousekeeping() { bme_status.temperature, bme_status.iaq, bme_status.iaq_accuracy); #endif +// generate DCF77 timeframes +#ifdef HAS_DCF77 + sendDCF77(); +#endif + // check free heap memory if (ESP.getMinFreeHeap() <= MEM_LOW) { ESP_LOGI(TAG, @@ -86,8 +95,8 @@ void doHousekeeping() { "free heap = %d bytes)", ESP.getMinFreeHeap(), ESP.getFreeHeap()); SendPayload(COUNTERPORT, prio_high); // send data before clearing counters - reset_counters(); // clear macs container and reset all counters - get_salt(); // get new salt for salting hashes + reset_counters(); // clear macs container and reset all counters + get_salt(); // get new salt for salting hashes if (ESP.getMinFreeHeap() <= MEM_LOW) // check again do_reset(); // memory leak, reset device @@ -98,8 +107,8 @@ void doHousekeeping() { if (ESP.getMinFreePsram() <= MEM_LOW) { ESP_LOGI(TAG, "PSRAM full, counter cleared"); SendPayload(COUNTERPORT, prio_high); // send data before clearing counters - reset_counters(); // clear macs container and reset all counters - get_salt(); // get new salt for salting hashes + reset_counters(); // clear macs container and reset all counters + get_salt(); // get new salt for salting hashes if (ESP.getMinFreePsram() <= MEM_LOW) // check again do_reset(); // memory leak, reset device diff --git a/src/dcf77.cpp b/src/dcf77.cpp new file mode 100644 index 00000000..0113e4b7 --- /dev/null +++ b/src/dcf77.cpp @@ -0,0 +1,219 @@ +// +// source: +// https://www.elektormagazine.com/labs/dcf77-emulator-with-esp8266-elektor-labs-version-150713 +// +/* + Simulate 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 + some clock model syncronization of the beginning frame. After the three pulses + train one more single pulse is sent to safely close the frame +*/ + +#if defined HAS_DCF77 + +#include "dcf77.h" + +// Local logging tag +static const char TAG[] = "main"; + +TaskHandle_t DCF77Task; +QueueHandle_t DCFSendQueue; +hw_timer_t *dcfCycle = NULL; + +#define DCF77_FRAME_SIZE 60 +#define DCF_FRAME_QUEUE_SIZE (HOMECYCLE / 60 + 1) + +// array of dcf pulses for three minutes +uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; + +// initialize and configure DCF77 output +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); + digitalWrite(HAS_DCF77, LOW); + + return 1; // success + +} // 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 pArray[]) { + + uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); + uint8_t parity = 0; + + for (uint8_t n = startpos; n <= endpos; n++) { + pArray[n] = (data & 1) ? dcf_one : dcf_zero; + parity += (data & 1); + data >>= 1; + } + + return parity; +} + +void enqueueTimeframe(time_t t) { + + uint8_t ParityCount; + + // ENCODE HEAD + // bits 0..19 initialized with zeros + for (int n = 0; n <= 19; n++) + DCFtimeframe[n] = dcf_zero; + // bits 17..18: adjust for DayLightSaving + DCFtimeframe[18 - (myTZ.locIsDST(t) ? 1 : 0)] = dcf_one; + // bit 20: must be 1 to indicate time active + DCFtimeframe[20] = dcf_one; + + // ENCODE MINUTE (bits 21..28) + ParityCount = dec2bcd(minute(t), 21, 27, DCFtimeframe); + DCFtimeframe[28] = (ParityCount & 1) ? dcf_one : dcf_zero; + + // ENCODE HOUR (bits 29..35) + ParityCount = dec2bcd(hour(t), 29, 34, DCFtimeframe); + DCFtimeframe[35] = (ParityCount & 1) ? dcf_one : dcf_zero; + + // ENCODE DATE (bits 36..58) + ParityCount = dec2bcd(day(t), 36, 41, DCFtimeframe); + ParityCount += + dec2bcd((weekday(t) - 1) ? (weekday(t) - 1) : 7, 42, 44, DCFtimeframe); + ParityCount += dec2bcd(month(t), 45, 49, DCFtimeframe); + ParityCount += + dec2bcd(year(t) - 2000, 50, 57, + DCFtimeframe); // yes, we have a millenium 3000 bug here ;-) + DCFtimeframe[58] = (ParityCount & 1) ? dcf_one : dcf_zero; + + // ENCODE TAIL (bit 59) + DCFtimeframe[59] = dcf_off; + // --> missing code here for switching 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 + char out[DCF77_FRAME_SIZE + 1]; + uint8_t i; + for (i = 0; i < DCF77_FRAME_SIZE; i++) { + out[i] = DCFtimeframe[i] + '0'; // convert int digit to printable ascii + } + out[DCF77_FRAME_SIZE] = '\0'; // string termination char + ESP_LOGD(TAG, "DCF=%s", out); +} + +void sendDCF77() { + + time_t t = now(); + + /* + if (second(t) > 56) { + delay(30000); + return; + } + */ + + // enqueue DCF timeframes for each i minute + for (uint8_t i = 0; i < DCF_FRAME_QUEUE_SIZE; i++) + enqueueTimeframe(t + i * 60); + + /* + // how many to the minute end ? + // don't forget that we begin transmission at second 58 + delay((58 - second(t)) * 1000); + + // three minutes are needed to transmit all the packet + // then wait more 30 secs to locate safely at the half of minute + // NB 150+60=210sec, 60secs are lost from main routine + delay(150000); + */ + +} // Ende ReadAndDecodeTime() + +// interrupt service routine triggered each 100ms by ESP32 hardware timer +void IRAM_ATTR DCF77IRQ() { + xTaskNotifyFromISR(DCF77Task, xTaskGetTickCountFromISR(), eSetBits, NULL); + portYIELD_FROM_ISR(); +} + +#endif // HAS_DCF77 \ No newline at end of file diff --git a/src/display.cpp b/src/display.cpp index 280528d1..0c186a80 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -145,12 +145,14 @@ void refreshtheDisplay() { uint8_t msgWaiting; char buff[16]; // 16 chars line buffer +#if defined HAS_RTC || defined HAS_GPS const char timeNosyncSymbol = '?'; -#ifdef HAS_IF482 +#if defined HAS_IF482 || defined HAS_DCF77 const char timesyncSymbol = '+'; #else const char timesyncSymbol = '*'; #endif +#endif // HAS_RTC // update counter (lines 0-1) snprintf( @@ -214,17 +216,16 @@ void refreshtheDisplay() { u8x8.printf("%4dKB", getFreeRAM() / 1024); #ifdef HAS_LORA - u8x8.setCursor(0, 6); -#ifndef HAS_RTC +#if (!defined HAS_RTC) && (!defined HAS_GPS) // update LoRa status display (line 6) u8x8.printf("%-16s", display_line6); -#else +#else // HAS_RTC or HAS_GPS // update time/date display (line 6) time_t t = myTZ.toLocal(now()); char timeState = timeStatus() == timeSet ? timesyncSymbol : timeNosyncSymbol; -#ifdef RTC_INT // make timestatus symbol blinking +#ifdef RTC_INT // make timestatus symbol blinking if pps line if (second(t) % 2) timeState = ' '; #endif // RTC_INT diff --git a/src/gpsread.cpp b/src/gpsread.cpp index d6c58b99..a446118e 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -73,7 +73,7 @@ time_t get_gpstime(void) { if ((gps.time.age() < 1500) && (gps.time.isValid())) { t = tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second()); - ESP_LOGD(TAG, "GPS time: %d/%d/%d %d:%d:%d", year(t), month(t), day(t), + ESP_LOGD(TAG, "GPS time: %4d/%02d/%02d %02d:%02d:%02d", year(t), month(t), day(t), hour(t), minute(t), second(t)); } else { ESP_LOGW(TAG, "GPS has no confident time"); diff --git a/src/hal/generic.h b/src/hal/generic.h index 2b34967f..2997257a 100644 --- a/src/hal/generic.h +++ b/src/hal/generic.h @@ -55,6 +55,9 @@ // Settings for IF482 interface #define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters +// Settings for DCF77 interface +#define HAS_DCF77 GPIO_NUM_13 + // Pins for LORA chip SPI interface, reset line and interrupt lines #define LORA_SCK (5) #define LORA_CS (18) diff --git a/src/main.cpp b/src/main.cpp index 2a029a1f..4392c42a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,7 +28,8 @@ Uused tasks and timers: Task Core Prio Purpose ==================================================================================== ledloop 0 3 blinks LEDs -if482loop 1 3 serial feed of IF482 time telegrams +if482loop 0 3 generates serial feed of IF482 time telegrams +dcf77loop 0 3 generates DCF77 timeframe pulses spiloop 0 2 reads/writes data on spi interface IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer @@ -46,13 +47,13 @@ Tasks using i2c bus all must have same priority, because using mutex semaphore ESP32 hardware timers ================================ 0 triggers display refresh - 1 unused (reserved for DCF77) + 1 triggers DCF77 clock signal 2 triggers send payload cycle 3 triggers housekeeping cycle RTC hardware timer (if present) ================================ - triggers IF482 clock generator + triggers IF482 clock signal */ @@ -64,8 +65,12 @@ char display_line6[16], display_line7[16]; // display buffers uint8_t volatile channel = 0; // channel rotation counter uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, batt_voltage = 0; // globals for display -hw_timer_t *channelSwitch = NULL, *sendCycle = NULL, *homeCycle = NULL, - *displaytimer = NULL; // irq tasks + +hw_timer_t *sendCycle = NULL, *homeCycle = NULL; +#ifdef HAS_DISPLAY +hw_timer_t *displaytimer = NULL; +#endif + TaskHandle_t irqHandlerTask; SemaphoreHandle_t I2Caccess; @@ -327,6 +332,16 @@ void setup() { setSyncInterval(TIME_SYNC_INTERVAL_RTC * 60); #endif // HAS_RTC +#if defined HAS_DCF77 + strcat_P(features, " DCF77"); + assert(dcf77_init()); +#endif + +#if defined HAS_IF482 && defined RTC_INT + strcat_P(features, " IF482"); + assert(if482_init()); +#endif + // show compiled features ESP_LOGI(TAG, "Features:%s", features); @@ -403,8 +418,6 @@ void setup() { #endif #if defined HAS_IF482 && defined RTC_INT - strcat_P(features, " IF482"); - assert(if482_init()); ESP_LOGI(TAG, "Starting IF482 Generator..."); xTaskCreatePinnedToCore(if482_loop, // task function "if482loop", // name of task @@ -419,6 +432,24 @@ void setup() { attachInterrupt(digitalPinToInterrupt(RTC_INT), IF482IRQ, FALLING); #endif +#if defined HAS_DCF77 + ESP_LOGI(TAG, "Starting DCF77 Generator..."); + 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 + + // 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 + } // setup() void loop() { From 17cd82da686440d9e41a3f8a2fab59e9441f43a0 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Mon, 4 Feb 2019 20:02:30 +0100 Subject: [PATCH 05/23] v1.7..152: DCF77 fixes (experimental) --- include/cyclic.h | 4 - include/globals.h | 1 + platformio.ini | 12 +-- src/cyclic.cpp | 5 -- src/dcf77.cpp | 209 +++++++++++++++++++--------------------------- src/display.cpp | 17 ++-- src/if482.cpp | 18 ++++ src/lmic_config.h | 2 +- src/main.cpp | 43 +++------- 9 files changed, 132 insertions(+), 179 deletions(-) diff --git a/include/cyclic.h b/include/cyclic.h index c98ed050..007c09d8 100644 --- a/include/cyclic.h +++ b/include/cyclic.h @@ -15,10 +15,6 @@ #include "rtctime.h" #endif -#ifdef HAS_DCF77 -#include "dcf77.h" -#endif - void doHousekeeping(void); uint64_t uptime(void); void reset_counters(void); diff --git a/include/globals.h b/include/globals.h index 73c11287..8755b1f0 100644 --- a/include/globals.h +++ b/include/globals.h @@ -102,6 +102,7 @@ extern uint16_t volatile macs_total, macs_wifi, macs_ble, batt_voltage; // display values extern hw_timer_t *sendCycle, *displaytimer; extern SemaphoreHandle_t I2Caccess; +extern bool volatile BitsPending; extern std::set, Mallocator> macs; extern std::array::iterator it; diff --git a/platformio.ini b/platformio.ini index d64d1be7..462ed686 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,7 +6,7 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -;env_default = generic +env_default = generic ;env_default = ebox ;env_default = eboxtube ;env_default = heltec @@ -24,16 +24,16 @@ ;env_default = lolin32lora ;env_default = lolin32lite ;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. [common] ; 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! ; 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_protocol = esptool ;upload_protocol = custom @@ -45,9 +45,9 @@ monitor_speed = 115200 lib_deps_lora = MCCI LoRaWAN LMIC library@^2.3.1 lib_deps_display = - U8g2@>=2.25.5 + U8g2@>=2.25.7 lib_deps_rgbled = - SmartLeds@>=1.1.3 + SmartLeds@>=1.1.5 lib_deps_gps = TinyGPSPlus@>=1.0.2 lib_deps_rtc = diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 5b23817d..b4e6e9ff 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -83,11 +83,6 @@ void doHousekeeping() { bme_status.temperature, bme_status.iaq, bme_status.iaq_accuracy); #endif -// generate DCF77 timeframes -#ifdef HAS_DCF77 - sendDCF77(); -#endif - // check free heap memory if (ESP.getMinFreeHeap() <= MEM_LOW) { ESP_LOGI(TAG, diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 0113e4b7..b0a97c2d 100644 --- a/src/dcf77.cpp +++ b/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 - 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 - some clock model syncronization of the beginning frame. After the three pulses - train one more single pulse is sent to safely close the frame +// Emulate a DCF77 radio receiver +// +// parts of this code werde adapted from source: +// +https://www.elektormagazine.com/labs/dcf77-emulator-with-esp8266-elektor-labs-version-150713 +// */ #if defined HAS_DCF77 @@ -18,11 +15,9 @@ static const char TAG[] = "main"; TaskHandle_t DCF77Task; -QueueHandle_t DCFSendQueue; hw_timer_t *dcfCycle = NULL; #define DCF77_FRAME_SIZE 60 -#define DCF_FRAME_QUEUE_SIZE (HOMECYCLE / 60 + 1) // array of dcf pulses for three minutes uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; @@ -30,84 +25,31 @@ uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; // initialize and configure DCF77 output 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); - 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 } // 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 pArray[]) { @@ -123,7 +65,7 @@ uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, return parity; } -void enqueueTimeframe(time_t t) { +void generateTimeframe(time_t t) { uint8_t ParityCount; @@ -156,21 +98,7 @@ void enqueueTimeframe(time_t t) { // ENCODE TAIL (bit 59) DCFtimeframe[59] = dcf_off; - // --> missing code here for switching 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"); + // !! missing code here for leap second !! // for debug: print the DCF77 frame buffer 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[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 (second(t) > 56) { - delay(30000); + if (!BitsPending) { + // prepare next frame to send + generateTimeframe(now()); + BitsPending = true; + // wait until next minute, then kick off hardware timer and first DCF pulse + do { + delay(2); + } while (second()); + } + + // ticker out current frame + while (BitsPending) { + switch (pulse++) { + + case 0: // start of second -> start of timeframe for logic signal + if (DCFtimeframe[bit] != dcf_off) + digitalWrite(HAS_DCF77, LOW); return; - } - */ - // enqueue DCF timeframes for each i minute - for (uint8_t i = 0; i < DCF_FRAME_QUEUE_SIZE; i++) - enqueueTimeframe(t + i * 60); + case 1: // 100ms after start of second -> end of timeframe for logic 0 + if (DCFtimeframe[bit] == dcf_zero) + digitalWrite(HAS_DCF77, HIGH); + return; - /* - // how many to the minute end ? - // don't forget that we begin transmission at second 58 - delay((58 - second(t)) * 1000); + case 2: // 200ms after start of second -> end of timeframe for logic 1 + digitalWrite(HAS_DCF77, HIGH); + return; - // three minutes are needed to transmit all the packet - // then wait more 30 secs to locate safely at the half of minute - // NB 150+60=210sec, 60secs are lost from main routine - delay(150000); - */ + 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; -} // Ende ReadAndDecodeTime() + }; // switch + }; // while + +} // DCF_Out() // interrupt service routine triggered each 100ms by ESP32 hardware timer void IRAM_ATTR DCF77IRQ() { - xTaskNotifyFromISR(DCF77Task, xTaskGetTickCountFromISR(), eSetBits, NULL); + xTaskNotifyFromISR(DCF77Task, 0, eNoAction, NULL); 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 \ No newline at end of file diff --git a/src/display.cpp b/src/display.cpp index 0c186a80..552e5b25 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -145,14 +145,14 @@ void refreshtheDisplay() { uint8_t msgWaiting; char buff[16]; // 16 chars line buffer -#if defined HAS_RTC || defined HAS_GPS +#if (defined HAS_DCF77) || (defined HAS_IF482) const char timeNosyncSymbol = '?'; -#if defined HAS_IF482 || defined HAS_DCF77 +#if (defined HAS_IF482) const char timesyncSymbol = '+'; #else const char timesyncSymbol = '*'; #endif -#endif // HAS_RTC +#endif // update counter (lines 0-1) snprintf( @@ -217,21 +217,20 @@ void refreshtheDisplay() { #ifdef HAS_LORA u8x8.setCursor(0, 6); -#if (!defined HAS_RTC) && (!defined HAS_GPS) +#if (!defined HAS_DCF77) && (!defined HAS_IF482) // update LoRa status display (line 6) 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) time_t t = myTZ.toLocal(now()); char timeState = timeStatus() == timeSet ? timesyncSymbol : timeNosyncSymbol; -#ifdef RTC_INT // make timestatus symbol blinking if pps line - if (second(t) % 2) + // make timestatus symbol blinking if pps line + if ((BitsPending) && (second(t) % 2)) timeState = ' '; -#endif // RTC_INT u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t), timeState, day(t), printmonth[month(t)]); -#endif // HAS_RTC +#endif // update LMiC event display (line 7) u8x8.setCursor(0, 7); diff --git a/src/if482.cpp b/src/if482.cpp index b575b9b7..0d3d0bb8 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -103,7 +103,20 @@ int if482_init(void) { ESP_LOGE(TAG, "I2c bus busy - IF482 initialization error"); 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); + attachInterrupt(digitalPinToInterrupt(RTC_INT), IF482IRQ, FALLING); + return 1; } // if482_init @@ -134,6 +147,8 @@ String if482Telegram(time_t tt) { month(t), day(t), weekday(t), hour(t), minute(t), second(t)); snprintf(out, sizeof out, "O%cL%s\r", mon, buf); + ESP_LOGD(TAG, "IF482 = %s", out); + return out; } @@ -152,6 +167,8 @@ void if482_loop(void *pvParameters) { do { tt = now(); } while (t == tt); + + BitsPending = true; // start blink in display // take timestamp at moment of start of new second 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 IF482.print(if482Telegram(now() + 1)); } + BitsPending = false; // stop blink in display vTaskDelete(IF482Task); // shoud never be reached } // if482_loop() diff --git a/src/lmic_config.h b/src/lmic_config.h index ea9c532b..a28f24a5 100644 --- a/src/lmic_config.h +++ b/src/lmic_config.h @@ -34,7 +34,7 @@ // 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 // 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 // RF settings used during transmission and reception. Set to 2 to diff --git a/src/main.cpp b/src/main.cpp index 4392c42a..ff7a0d19 100644 --- a/src/main.cpp +++ b/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 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 bmeloop 1 1 reads data from BME sensor via i2c 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 (irqhandler, bmeloop) -ESP32 hardware timers +ESP32 hardware irq timers ================================ 0 triggers display refresh 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 uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, batt_voltage = 0; // globals for display +bool volatile BitsPending = false; // DCF77 or IF482 ticker indicator hw_timer_t *sendCycle = NULL, *homeCycle = NULL; #ifdef HAS_DISPLAY @@ -97,7 +98,7 @@ void setup() { char features[100] = ""; I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus - if ((I2Caccess) != NULL) + if (I2Caccess) xSemaphoreGive((I2Caccess)); // Flag the i2c bus available for use // disable brownout detection @@ -334,7 +335,6 @@ void setup() { #if defined HAS_DCF77 strcat_P(features, " DCF77"); - assert(dcf77_init()); #endif #if defined HAS_IF482 && defined RTC_INT @@ -417,37 +417,14 @@ void setup() { setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60); #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..."); - 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 - - // 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 + assert(if482_init()); +#elif defined HAS_DCF77 ESP_LOGI(TAG, "Starting DCF77 Generator..."); - 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 - - // 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); + assert(dcf77_init()); #endif } // setup() From 56ab5a24a5e5477fb6ffba003e2cff97647e68e3 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Mon, 4 Feb 2019 20:40:46 +0100 Subject: [PATCH 06/23] display.cpp: compiler warning sanitized --- src/display.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/display.cpp b/src/display.cpp index 552e5b25..75816b37 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -42,8 +42,8 @@ const char lora_datarate[] = {"121110090807FSNA"}; #endif // helper arry for converting month values to text -char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +const char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; uint8_t volatile DisplayState = 0; From 8d01b651bb8f2579fc4ec97e11f7faa4be432637 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Mon, 4 Feb 2019 21:42:44 +0100 Subject: [PATCH 07/23] IF482 bugfix --- src/if482.cpp | 28 ++++++++++++++-------------- src/main.cpp | 7 +++---- src/paxcounter.conf | 2 +- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/if482.cpp b/src/if482.cpp index 0d3d0bb8..19fd75af 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -88,9 +88,12 @@ TaskHandle_t IF482Task; HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) -// initialize and configure GPS +// initialize and configure IF482 Generator int if482_init(void) { + // setup external interupt for active low RTC INT pin + pinMode(RTC_INT, INPUT_PULLUP); + // open serial interface IF482.begin(HAS_IF482); @@ -113,8 +116,6 @@ int if482_init(void) { 0); // CPU core assert(IF482Task); // has if482loop task started? - // setup external interupt for active low RTC INT pin - pinMode(RTC_INT, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(RTC_INT), IF482IRQ, FALLING); return 1; @@ -124,10 +125,7 @@ int if482_init(void) { String if482Telegram(time_t tt) { time_t t = myTZ.toLocal(tt); - - char mon; - char buf[14] = "000000F000000"; - char out[17]; + char mon, buf[14], out[17]; switch (timeStatus()) { // indicates if time has been set and recently synced case timeSet: // time is set and is synced @@ -141,14 +139,16 @@ String if482Telegram(time_t tt) { break; } // switch - if ((timeStatus() == timeSet) || - (timeStatus() == timeNeedsSync)) // do we have valid time? - snprintf(buf, sizeof buf, "%02u%02u%02u%1u%02u%02u%02u", year(t) - 2000, + // 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, month(t), day(t), weekday(t), hour(t), minute(t), second(t)); + else + snprintf(buf, sizeof(buf), "000000F000000"); // no confident time/date - snprintf(out, sizeof out, "O%cL%s\r", mon, buf); + // output IF482 telegram + snprintf(out, sizeof(out), "O%cL%s\r", mon, buf); ESP_LOGD(TAG, "IF482 = %s", out); - return out; } @@ -167,7 +167,7 @@ void if482_loop(void *pvParameters) { do { tt = now(); } while (t == tt); - + BitsPending = true; // start blink in display // take timestamp at moment of start of new second @@ -186,7 +186,7 @@ void if482_loop(void *pvParameters) { vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot IF482.print(if482Telegram(now() + 1)); } - BitsPending = false; // stop blink in display + BitsPending = false; // stop blink in display vTaskDelete(IF482Task); // shoud never be reached } // if482_loop() diff --git a/src/main.cpp b/src/main.cpp index ff7a0d19..a654c5a5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -337,9 +337,8 @@ void setup() { strcat_P(features, " DCF77"); #endif -#if defined HAS_IF482 && defined RTC_INT +#if (defined HAS_IF482) && (defined RTC_INT) strcat_P(features, " IF482"); - assert(if482_init()); #endif // show compiled features @@ -417,9 +416,9 @@ void setup() { setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60); #endif -#if defined HAS_IF482 && defined DCF_77 +#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 +#elif (defined HAS_IF482) && (defined RTC_INT) ESP_LOGI(TAG, "Starting IF482 Generator..."); assert(if482_init()); #elif defined HAS_DCF77 diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 9ca0766e..c796b1d8 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -86,7 +86,7 @@ #define TIME_SYNC_INTERVAL_RTC 60 // sync time each .. minutes from RTC [default = 60], comment out means off #define TIME_WRITE_INTERVAL_RTC 60 // write time each .. minutes from GPS/LORA to RTC [default = 60], comment out means off //#define TIME_SYNC_INTERVAL_LORA 60 // sync time each .. minutes from LORA network [default = 60], comment out means off -#define IF482_OFFSET 984 // 1sec minus IF482 serial transmit time [ms]: e.g. 9 bits * 17 bytes * 1/9600 bps = 16ms +#define IF482_OFFSET 16 // IF482 serial transmit time [ms]: e.g. 9 bits * 17 bytes * 1/9600 bps = 16ms // time zone, see https://github.com/JChristensen/Timezone/blob/master/examples/WorldClock/WorldClock.ino #define DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time From 82489524d35e2e64dd600ad336896892d79c1d57 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Mon, 4 Feb 2019 23:42:17 +0100 Subject: [PATCH 08/23] dcf77.cpp bugfixes and now finalized --- platformio.ini | 6 ++-- src/dcf77.cpp | 69 +++++++++++++++++++++++++++------------------- src/hal/ttgobeam.h | 15 ++++++---- 3 files changed, 53 insertions(+), 37 deletions(-) diff --git a/platformio.ini b/platformio.ini index 462ed686..dc5b8873 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,7 +6,7 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -env_default = generic +;env_default = generic ;env_default = ebox ;env_default = eboxtube ;env_default = heltec @@ -15,7 +15,7 @@ env_default = generic ;env_default = ttgov2 ;env_default = ttgov21old ;env_default = ttgov21new -;env_default = ttgobeam +env_default = ttgobeam ;env_default = ttgofox ;env_default = lopy ;env_default = lopy4 @@ -33,7 +33,7 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng release_version = 1.7.152 ; 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 -debug_level = 0 +debug_level = 4 ; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA upload_protocol = esptool ;upload_protocol = custom diff --git a/src/dcf77.cpp b/src/dcf77.cpp index b0a97c2d..9ac7e3c8 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -25,6 +25,8 @@ uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; // initialize and configure DCF77 output int dcf77_init(void) { + BitsPending = false; + pinMode(HAS_DCF77, OUTPUT); digitalWrite(HAS_DCF77, HIGH); @@ -42,9 +44,13 @@ int dcf77_init(void) { 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 + timerAlarmWrite(dcfCycle, 1000, true); // 100ms cycle + + // wait until beginning of next minute, then start DCF pulse + do { + delay(2); + } while (second()); timerAlarmEnable(dcfCycle); - xTaskNotify(DCF77Task, 0, eNoAction); return 1; // success @@ -100,62 +106,69 @@ void generateTimeframe(time_t t) { DCFtimeframe[59] = dcf_off; // !! missing code here for leap second !! - // for debug: print the DCF77 frame buffer - char out[DCF77_FRAME_SIZE + 1]; - uint8_t i; - for (i = 0; i < DCF77_FRAME_SIZE; i++) { - out[i] = DCFtimeframe[i] + '0'; // convert int digit to printable ascii - } - out[DCF77_FRAME_SIZE] = '\0'; // string termination char - ESP_LOGD(TAG, "DCF Timeframe = %s", out); + /* + // for debug: print the DCF77 frame buffer + char out[DCF77_FRAME_SIZE + 1]; + uint8_t i; + for (i = 0; i < DCF77_FRAME_SIZE; i++) { + out[i] = DCFtimeframe[i] + '0'; // convert int digit to printable ascii + } + out[DCF77_FRAME_SIZE] = '\0'; // string termination char + ESP_LOGD(TAG, "DCF Timeframe = %s", out); + */ } -// called every 100msec by hardware time +// helper function to convert gps date/time into time_t +time_t nextMinute(time_t t) { + tmElements_t tm; + breakTime(t, tm); + tm.Minute++; + tm.Second = 0; + return makeTime(tm); +} + +// called every 100msec by hardware timer to pulse out DCF signal void DCF_Out() { static uint8_t bit = 0; static uint8_t pulse = 0; if (!BitsPending) { - // prepare next frame to send - generateTimeframe(now()); + // prepare frame for next minute to send + generateTimeframe(nextMinute(now())); + // start blinking symbol on display and kick off timer BitsPending = true; - // wait until next minute, then kick off hardware timer and first DCF pulse - do { - delay(2); - } while (second()); } - // ticker out current frame - while (BitsPending) { + // ticker out current DCF frame + if (BitsPending) { switch (pulse++) { case 0: // start of second -> start of timeframe for logic signal if (DCFtimeframe[bit] != dcf_off) digitalWrite(HAS_DCF77, LOW); - return; + break; case 1: // 100ms after start of second -> end of timeframe for logic 0 if (DCFtimeframe[bit] == dcf_zero) digitalWrite(HAS_DCF77, HIGH); - return; + break; case 2: // 200ms after start of second -> end of timeframe for logic 1 digitalWrite(HAS_DCF77, HIGH); - return; + break; case 9: // last pulse before next second starts pulse = 0; - if (bit++ != DCF77_FRAME_SIZE) - return; - else { // end of DCF77 frame (59th second) + if (bit++ == (DCF77_FRAME_SIZE - 1)) // end of DCF77 frame (59th second) + { bit = 0; BitsPending = false; }; break; }; // switch - }; // while + }; // if } // DCF_Out() @@ -179,8 +192,8 @@ void dcf77_loop(void *pvParameters) { DCF_Out(); } - BitsPending = false; // stop blink in display - vTaskDelete(DCF77Task); // shoud never be reached + BitsPending = false; // stop blink in display, should never be reached + vTaskDelete(DCF77Task); } // dcf77_loop() #endif // HAS_DCF77 \ No newline at end of file diff --git a/src/hal/ttgobeam.h b/src/hal/ttgobeam.h index 8dd12bd4..f49eb27e 100644 --- a/src/hal/ttgobeam.h +++ b/src/hal/ttgobeam.h @@ -24,16 +24,19 @@ // enable only if device has these sensors, otherwise comment these lines // BME680 sensor on I2C bus -//#define HAS_BME SDA, SCL -//#define BME_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !! +#define HAS_BME SDA, SCL +#define BME_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !! // display (if connected) -//#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C -//#define MY_OLED_SDA SDA -//#define MY_OLED_SCL SCL -//#define MY_OLED_RST U8X8_PIN_NONE +#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C +#define MY_OLED_SDA SDA +#define MY_OLED_SCL SCL +#define MY_OLED_RST U8X8_PIN_NONE //#define DISPLAY_FLIP 1 // use if display is rotated +// Settings for DCF77 interface +#define HAS_DCF77 GPIO_NUM_13 + // user defined sensors (if connected) //#define HAS_SENSORS 1 // comment out if device has user defined sensors From 3c913095a231fc800508298432c4fa55a6e114fb Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Tue, 5 Feb 2019 23:50:05 +0100 Subject: [PATCH 09/23] DCF77 fixes --- include/dcf77.h | 7 +++- src/dcf77.cpp | 97 ++++++++++++++++++++++++++++------------------- src/hal/generic.h | 3 +- 3 files changed, 67 insertions(+), 40 deletions(-) diff --git a/include/dcf77.h b/include/dcf77.h index bf2ffcee..f9a64e61 100644 --- a/include/dcf77.h +++ b/include/dcf77.h @@ -7,10 +7,15 @@ extern TaskHandle_t DCF77Task; extern hw_timer_t *dcfCycle; enum dcf_pulses { dcf_off, dcf_zero, dcf_one }; +enum dcf_pinstate { dcf_low, dcf_high }; +void IRAM_ATTR DCF77IRQ(void); int dcf77_init(void); void dcf77_loop(void *pvParameters); -void IRAM_ATTR DCF77IRQ(void); void sendDCF77(void); +void DCF_Out(uint8_t startsec); +void generateTimeframe(time_t t); +void set_DCF77_pin(dcf_pinstate state); +uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, uint8_t pArray[]); #endif \ No newline at end of file diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 9ac7e3c8..95c14c76 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -2,9 +2,11 @@ // Emulate a DCF77 radio receiver // // parts of this code werde adapted from source: -// https://www.elektormagazine.com/labs/dcf77-emulator-with-esp8266-elektor-labs-version-150713 // +// a nice & free logic test program for DCF77 can be found here: +https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/ +// */ #if defined HAS_DCF77 @@ -25,10 +27,11 @@ uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; // initialize and configure DCF77 output int dcf77_init(void) { + time_t t, tt; BitsPending = false; pinMode(HAS_DCF77, OUTPUT); - digitalWrite(HAS_DCF77, HIGH); + set_DCF77_pin(dcf_low); xTaskCreatePinnedToCore(dcf77_loop, // task function "dcf77loop", // name of task @@ -46,34 +49,25 @@ int dcf77_init(void) { timerAttachInterrupt(dcfCycle, &DCF77IRQ, true); timerAlarmWrite(dcfCycle, 1000, true); // 100ms cycle - // wait until beginning of next minute, then start DCF pulse + // wait until beginning of next second, then kick off first DCF pulse and + // start timer interrupt + + t = tt = now(); do { - delay(2); - } while (second()); + tt = now(); + } while (t == tt); + + DCF_Out(second(tt)); timerAlarmEnable(dcfCycle); return 1; // success } // ifdcf77_init -uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, - uint8_t pArray[]) { - - uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); - uint8_t parity = 0; - - for (uint8_t n = startpos; n <= endpos; n++) { - pArray[n] = (data & 1) ? dcf_one : dcf_zero; - parity += (data & 1); - data >>= 1; - } - - return parity; -} - -void generateTimeframe(time_t t) { +void generateTimeframe(time_t tt) { uint8_t ParityCount; + time_t t = myTZ.toLocal(tt); // convert to local time // ENCODE HEAD // bits 0..19 initialized with zeros @@ -118,24 +112,15 @@ void generateTimeframe(time_t t) { */ } -// helper function to convert gps date/time into time_t -time_t nextMinute(time_t t) { - tmElements_t tm; - breakTime(t, tm); - tm.Minute++; - tm.Second = 0; - return makeTime(tm); -} - // called every 100msec by hardware timer to pulse out DCF signal -void DCF_Out() { +void DCF_Out(uint8_t startsec) { - static uint8_t bit = 0; + static uint8_t bit = startsec; static uint8_t pulse = 0; if (!BitsPending) { - // prepare frame for next minute to send - generateTimeframe(nextMinute(now())); + // prepare frame to send for next minute + generateTimeframe(now() + 61); // start blinking symbol on display and kick off timer BitsPending = true; } @@ -146,16 +131,16 @@ void DCF_Out() { case 0: // start of second -> start of timeframe for logic signal if (DCFtimeframe[bit] != dcf_off) - digitalWrite(HAS_DCF77, LOW); + set_DCF77_pin(dcf_low); break; case 1: // 100ms after start of second -> end of timeframe for logic 0 if (DCFtimeframe[bit] == dcf_zero) - digitalWrite(HAS_DCF77, HIGH); + set_DCF77_pin(dcf_high); break; case 2: // 200ms after start of second -> end of timeframe for logic 1 - digitalWrite(HAS_DCF77, HIGH); + set_DCF77_pin(dcf_high); break; case 9: // last pulse before next second starts @@ -190,10 +175,46 @@ void dcf77_loop(void *pvParameters) { NULL, portMAX_DELAY); // wait forever (missing error handling here...) - DCF_Out(); + DCF_Out(0); } BitsPending = false; // stop blink in display, should never be reached vTaskDelete(DCF77Task); } // dcf77_loop() +// helper function to convert decimal to bcd digit +uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, + uint8_t pArray[]) { + + uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); + uint8_t parity = 0; + + for (uint8_t n = startpos; n <= endpos; n++) { + pArray[n] = (data & 1) ? dcf_one : dcf_zero; + parity += (data & 1); + data >>= 1; + } + + return parity; +} + +// helper function to switch GPIO line with DCF77 signal +void set_DCF77_pin(dcf_pinstate state) { + switch (state) { + case dcf_low: +#ifdef DCF77_ACTIVE_LOW + digitalWrite(HAS_DCF77, HIGH); +#else + digitalWrite(HAS_DCF77, LOW); +#endif + break; + case dcf_high: +#ifdef DCF77_ACTIVE_LOW + digitalWrite(HAS_DCF77, LOW); +#else + digitalWrite(HAS_DCF77, HIGH); +#endif + break; + } // switch +} // DCF77_pulse + #endif // HAS_DCF77 \ No newline at end of file diff --git a/src/hal/generic.h b/src/hal/generic.h index 2997257a..8f2c2da4 100644 --- a/src/hal/generic.h +++ b/src/hal/generic.h @@ -56,7 +56,8 @@ #define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters // Settings for DCF77 interface -#define HAS_DCF77 GPIO_NUM_13 +#define HAS_DCF77 GPIO_NUM_1 +//#define DCF77_ACTIVE_LOW 1 // Pins for LORA chip SPI interface, reset line and interrupt lines #define LORA_SCK (5) From f5e5bf798a2aab1cc62ffffb3650d0749af6cb01 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 7 Feb 2019 07:32:55 +0100 Subject: [PATCH 10/23] DCF77 improvements --- README.md | 6 +++- src/dcf77.cpp | 83 ++++++++++++++++++++++++++++++++++------------- src/hal/generic.h | 1 + src/if482.cpp | 51 +++++++++++++++++++---------- src/main.cpp | 2 +- 5 files changed, 100 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index dbe9d425..19085256 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Depending on board hardware following features are supported: - GPS (Generic serial NMEA, or Quectel L76 I2C) - Environmental sensor (Bosch BME680 I2C) - Real Time Clock (Maxim DS3231 I2C) -- IF482 time telegram generator (serial port) +- IF482 (serial) and DCF77 (gpio) time telegram generator Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.
@@ -138,6 +138,10 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp - Red long blink: LoRaWAN stack error - White long blink: Known Beacon detected +# Clock controller + +Paxcounter can be used to sync a clock which has DCF77 or IF482 time telegram input with an external time source. Supported external time sources are GPS time, LORAWAN network time (v1.1) or on board RTC time. The precision of the generated DCF77 / IF482 signal depends on precision of used on board time base. Supported are both external time base (e.g. pps pin of GPS chip or oscillator output of RTC chip) and ESP32 internal clock. Selection of time base and clock frequency must be given by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h). + # Payload format You can select different payload formats in [paxcounter.conf](src/paxcounter.conf#L12): diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 95c14c76..f5bd53ec 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -19,7 +19,8 @@ static const char TAG[] = "main"; TaskHandle_t DCF77Task; hw_timer_t *dcfCycle = NULL; -#define DCF77_FRAME_SIZE 60 +#define DCF77_FRAME_SIZE (60) +#define DCF77_PULSE_DURATION (100) // array of dcf pulses for three minutes uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; @@ -43,14 +44,38 @@ int dcf77_init(void) { assert(DCF77Task); // has dcf77 task started? - // setup 100ms clock signal for DCF77 generator using esp32 hardware timer 1 - ESP_LOGD(TAG, "Starting DCF pulse..."); + // if we have hardware pps signal we use it as precise time base +#ifdef RTC_INT + +#ifndef RTC_CLK // assure we know external clock freq +#error "External clock cycle not defined in board hal file" +#endif + + // setup external interupt for active low RTC INT pin + pinMode(RTC_INT, INPUT_PULLUP); + + // setup external rtc 1Hz clock for triggering DCF77 telegram + ESP_LOGI(TAG, "Time base external clock"); + if (I2C_MUTEX_LOCK()) { + Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); + Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); + I2C_MUTEX_UNLOCK(); + } else { + ESP_LOGE(TAG, "I2c bus busy - RTC initialization error"); + return 0; // failure + } + +// if we don't have pps signal from RTC we emulate it using ESP32 hardware timer +#else +#define RTC_CLK (DCF77_PULSE_DURATION) // setup clock cycle + ESP_LOGI(TAG, "Time base ESP32 clock"); dcfCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec timerAttachInterrupt(dcfCycle, &DCF77IRQ, true); - timerAlarmWrite(dcfCycle, 1000, true); // 100ms cycle + timerAlarmWrite(dcfCycle, 10 * RTC_CLK, true); // RTC_CLK / 1sec = 100ms +#endif // wait until beginning of next second, then kick off first DCF pulse and - // start timer interrupt + // start clock signal t = tt = now(); do { @@ -58,10 +83,14 @@ int dcf77_init(void) { } while (t == tt); DCF_Out(second(tt)); + +#ifdef RTC_INT // start external clock + attachInterrupt(digitalPinToInterrupt(RTC_INT), DCF77IRQ, FALLING); +#else // start internal clock timerAlarmEnable(dcfCycle); +#endif return 1; // success - } // ifdcf77_init void generateTimeframe(time_t tt) { @@ -113,9 +142,9 @@ void generateTimeframe(time_t tt) { } // called every 100msec by hardware timer to pulse out DCF signal -void DCF_Out(uint8_t startsec) { +void DCF_Out(uint8_t startOffset) { - static uint8_t bit = startsec; + static uint8_t bit = startOffset; static uint8_t pulse = 0; if (!BitsPending) { @@ -143,7 +172,7 @@ void DCF_Out(uint8_t startsec) { set_DCF77_pin(dcf_high); break; - case 9: // last pulse before next second starts + case 9: // 900ms after start -> last pulse before next second starts pulse = 0; if (bit++ == (DCF77_FRAME_SIZE - 1)) // end of DCF77 frame (59th second) { @@ -154,31 +183,33 @@ void DCF_Out(uint8_t startsec) { }; // switch }; // if - } // DCF_Out() -// interrupt service routine triggered each 100ms by ESP32 hardware timer -void IRAM_ATTR DCF77IRQ() { - xTaskNotifyFromISR(DCF77Task, 0, eNoAction, NULL); - portYIELD_FROM_ISR(); -} - void dcf77_loop(void *pvParameters) { configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check + TickType_t wakeTime; + // 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, + 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...) - DCF_Out(0); - } - BitsPending = false; // stop blink in display, should never be reached - vTaskDelete(DCF77Task); +#if (!defined RTC_INT) || (RTC_CLK == DCF77_PULSE_DURATION) + DCF_Out(0); // we don't need clock rescaling + +#else // we need clock rescaling by software timer + for (uint8_t i = 1; i <= RTC_CLK / DCF77_PULSE_DURATION; i++) { + DCF_Out(0); + vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION)); + } // for +#endif + + } // for } // dcf77_loop() // helper function to convert decimal to bcd digit @@ -217,4 +248,10 @@ void set_DCF77_pin(dcf_pinstate state) { } // switch } // DCF77_pulse +// interrupt service routine triggered by external interrupt or internal timer +void IRAM_ATTR DCF77IRQ() { + xTaskNotifyFromISR(DCF77Task, xTaskGetTickCountFromISR(), eSetBits, NULL); + portYIELD_FROM_ISR(); +} + #endif // HAS_DCF77 \ No newline at end of file diff --git a/src/hal/generic.h b/src/hal/generic.h index 8f2c2da4..5db463a0 100644 --- a/src/hal/generic.h +++ b/src/hal/generic.h @@ -51,6 +51,7 @@ // Pins for on board DS3231 RTC chip #define HAS_RTC MY_OLED_SDA, MY_OLED_SCL // SDA, SCL #define RTC_INT GPIO_NUM_34 // interrupt input from rtc +#define RTC_CLK (1000) // frequency of RTC clock signal in ms // Settings for IF482 interface #define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters diff --git a/src/if482.cpp b/src/if482.cpp index 19fd75af..307de066 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -94,19 +94,7 @@ int if482_init(void) { // setup external interupt for active low RTC INT pin pinMode(RTC_INT, INPUT_PULLUP); - // open serial interface - IF482.begin(HAS_IF482); - - // use external rtc 1Hz clock for triggering IF482 telegram - if (I2C_MUTEX_LOCK()) { - Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); - Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); - I2C_MUTEX_UNLOCK(); - } else { - ESP_LOGE(TAG, "I2c bus busy - IF482 initialization error"); - return 0; - } - + // start if482 serial output feed task xTaskCreatePinnedToCore(if482_loop, // task function "if482loop", // name of task 2048, // stack size of task @@ -116,13 +104,42 @@ int if482_init(void) { 0); // CPU core assert(IF482Task); // has if482loop task started? + + // open serial interface + IF482.begin(HAS_IF482); + + // if we have hardware pps signal we use it as precise time base +#ifdef RTC_INT +// assure we know clock freq +#ifndef RTC_CLK +#error "No RTC clock cycle defined in board hal file" +#endif + // use external rtc 1Hz clock for triggering IF482 telegram + if (I2C_MUTEX_LOCK()) { + Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); + Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); + I2C_MUTEX_UNLOCK(); + } else { + ESP_LOGE(TAG, "I2c bus busy - IF482 initialization error"); + return 0; // failure + } attachInterrupt(digitalPinToInterrupt(RTC_INT), IF482IRQ, FALLING); - return 1; +// no RTC, thus we use less precise ESP32 hardware timer +#else + // setup 1000ms clock signal for IF482 generator using esp32 hardware timer 1 + ESP_LOGD(TAG, "Starting IF482 pulse..."); + dcfCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec + timerAttachInterrupt(dcfCycle, &IF482IRQ, true); + timerAlarmWrite(dcfCycle, 10000, true); // 1000ms cycle + timerAlarmEnable(dcfCycle); +#endif + + return 1; // success } // if482_init -String if482Telegram(time_t tt) { +String IF482_Out(time_t tt) { time_t t = myTZ.toLocal(tt); char mon, buf[14], out[17]; @@ -184,10 +201,8 @@ void if482_loop(void *pvParameters) { // now we're synced to start of second tt and wait // until it's time to start transmit telegram for tt+1 vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot - IF482.print(if482Telegram(now() + 1)); + IF482.print(IF482_Out(now() + 1)); } - BitsPending = false; // stop blink in display - vTaskDelete(IF482Task); // shoud never be reached } // if482_loop() // interrupt service routine triggered by RTC 1Hz precise clock diff --git a/src/main.cpp b/src/main.cpp index a654c5a5..288c5e61 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -418,7 +418,7 @@ void setup() { #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) +#elif defined HAS_IF482 ESP_LOGI(TAG, "Starting IF482 Generator..."); assert(if482_init()); #elif defined HAS_DCF77 From e1b6d9a04c94b4f995770b6d7ef73fca5d56b1c7 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 7 Feb 2019 20:39:32 +0100 Subject: [PATCH 11/23] DCF77 optimizations --- include/dcf77.h | 1 + src/dcf77.cpp | 215 ++++++++++++++++++++++++-------------------- src/paxcounter.conf | 3 +- 3 files changed, 120 insertions(+), 99 deletions(-) diff --git a/include/dcf77.h b/include/dcf77.h index f9a64e61..eef10541 100644 --- a/include/dcf77.h +++ b/include/dcf77.h @@ -17,5 +17,6 @@ void DCF_Out(uint8_t startsec); void generateTimeframe(time_t t); void set_DCF77_pin(dcf_pinstate state); uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, uint8_t pArray[]); +uint8_t sync_clock(time_t t); #endif \ No newline at end of file diff --git a/src/dcf77.cpp b/src/dcf77.cpp index f5bd53ec..579b1785 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -44,8 +44,7 @@ int dcf77_init(void) { assert(DCF77Task); // has dcf77 task started? - // if we have hardware pps signal we use it as precise time base -#ifdef RTC_INT +#ifdef RTC_INT // if we have hardware pps signal we use it as precise time base #ifndef RTC_CLK // assure we know external clock freq #error "External clock cycle not defined in board hal file" @@ -65,24 +64,20 @@ int dcf77_init(void) { return 0; // failure } -// if we don't have pps signal from RTC we emulate it using ESP32 hardware timer -#else +#else // if we don't have pps signal from RTC we use ESP32 hardware timer + #define RTC_CLK (DCF77_PULSE_DURATION) // setup clock cycle ESP_LOGI(TAG, "Time base ESP32 clock"); dcfCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec timerAttachInterrupt(dcfCycle, &DCF77IRQ, true); - timerAlarmWrite(dcfCycle, 10 * RTC_CLK, true); // RTC_CLK / 1sec = 100ms + timerAlarmWrite(dcfCycle, 10 * RTC_CLK, true); // 100ms + #endif // wait until beginning of next second, then kick off first DCF pulse and // start clock signal - t = tt = now(); - do { - tt = now(); - } while (t == tt); - - DCF_Out(second(tt)); + DCF_Out(sync_clock(now())); #ifdef RTC_INT // start external clock attachInterrupt(digitalPinToInterrupt(RTC_INT), DCF77IRQ, FALLING); @@ -93,6 +88,103 @@ int dcf77_init(void) { return 1; // success } // ifdcf77_init +// called every 100msec by hardware timer to pulse out DCF signal +void DCF_Out(uint8_t startOffset) { + + static uint8_t bit = startOffset; + static uint8_t pulse = 0; +#ifdef TIME_SYNC_INTERVAL_DCF + static uint32_t nextDCFsync = millis() + TIME_SYNC_INTERVAL_DCF * 60000; +#endif + + if (!BitsPending) { + // prepare frame to send for next minute + generateTimeframe(now() + DCF77_FRAME_SIZE + 1); + // start blinking symbol on display and kick off timer + BitsPending = true; + } + + // ticker out current DCF frame + if (BitsPending) { + switch (pulse++) { + + case 0: // start of second -> start of timeframe for logic signal + if (DCFtimeframe[bit] != dcf_off) + set_DCF77_pin(dcf_low); + break; + + case 1: // 100ms after start of second -> end of timeframe for logic 0 + if (DCFtimeframe[bit] == dcf_zero) + set_DCF77_pin(dcf_high); + break; + + case 2: // 200ms after start of second -> end of timeframe for logic 1 + set_DCF77_pin(dcf_high); + break; + + case 9: // 900ms after start -> last pulse before next second starts + pulse = 0; + if (bit++ == (DCF77_FRAME_SIZE - 1)) // end of DCF77 frame (59th second) + { + bit = 0; + BitsPending = false; +// recalibrate clock after a fixed timespan, do this in 59th second +#ifdef TIME_SYNC_INTERVAL_DCF + if ((millis() >= nextDCFsync)) { + sync_clock(now()); // in second 58,90x -> waiting for second 59 + nextDCFsync = millis() + TIME_SYNC_INTERVAL_DCF * + 60000; // set up next time sync period + } +#endif + }; + break; + + }; // switch + }; // if +} // DCF_Out() + +void dcf77_loop(void *pvParameters) { + + configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check + + TickType_t wakeTime; + + // 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 + &wakeTime, // receives moment of call from isr + portMAX_DELAY); // wait forever (missing error handling here...) + +#if (RTC_CLK == DCF77_PULSE_DURATION) + DCF_Out(0); // we don't need clock rescaling + +#else // we need clock rescaling by software timer + for (uint8_t i = 1; i <= RTC_CLK / DCF77_PULSE_DURATION; i++) { + DCF_Out(0); + vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION)); + } +#endif + } // for +} // dcf77_loop() + +// helper function to convert decimal to bcd digit +uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, + uint8_t pArray[]) { + + uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); + uint8_t parity = 0; + + for (uint8_t n = startpos; n <= endpos; n++) { + pArray[n] = (data & 1) ? dcf_one : dcf_zero; + parity += (data & 1); + data >>= 1; + } + + return parity; +} + void generateTimeframe(time_t tt) { uint8_t ParityCount; @@ -141,93 +233,6 @@ void generateTimeframe(time_t tt) { */ } -// called every 100msec by hardware timer to pulse out DCF signal -void DCF_Out(uint8_t startOffset) { - - static uint8_t bit = startOffset; - static uint8_t pulse = 0; - - if (!BitsPending) { - // prepare frame to send for next minute - generateTimeframe(now() + 61); - // start blinking symbol on display and kick off timer - BitsPending = true; - } - - // ticker out current DCF frame - if (BitsPending) { - switch (pulse++) { - - case 0: // start of second -> start of timeframe for logic signal - if (DCFtimeframe[bit] != dcf_off) - set_DCF77_pin(dcf_low); - break; - - case 1: // 100ms after start of second -> end of timeframe for logic 0 - if (DCFtimeframe[bit] == dcf_zero) - set_DCF77_pin(dcf_high); - break; - - case 2: // 200ms after start of second -> end of timeframe for logic 1 - set_DCF77_pin(dcf_high); - break; - - case 9: // 900ms after start -> last pulse before next second starts - pulse = 0; - if (bit++ == (DCF77_FRAME_SIZE - 1)) // end of DCF77 frame (59th second) - { - bit = 0; - BitsPending = false; - }; - break; - - }; // switch - }; // if -} // DCF_Out() - -void dcf77_loop(void *pvParameters) { - - configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check - - TickType_t wakeTime; - - // 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 - &wakeTime, // receives moment of call from isr - portMAX_DELAY); // wait forever (missing error handling here...) - -#if (!defined RTC_INT) || (RTC_CLK == DCF77_PULSE_DURATION) - DCF_Out(0); // we don't need clock rescaling - -#else // we need clock rescaling by software timer - for (uint8_t i = 1; i <= RTC_CLK / DCF77_PULSE_DURATION; i++) { - DCF_Out(0); - vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION)); - } // for -#endif - - } // for -} // dcf77_loop() - -// helper function to convert decimal to bcd digit -uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, - uint8_t pArray[]) { - - uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); - uint8_t parity = 0; - - for (uint8_t n = startpos; n <= endpos; n++) { - pArray[n] = (data & 1) ? dcf_one : dcf_zero; - parity += (data & 1); - data >>= 1; - } - - return parity; -} - // helper function to switch GPIO line with DCF77 signal void set_DCF77_pin(dcf_pinstate state) { switch (state) { @@ -248,6 +253,20 @@ void set_DCF77_pin(dcf_pinstate state) { } // switch } // DCF77_pulse +// helper function to sync phase of DCF output signal to start of second t +uint8_t sync_clock(time_t t) { + time_t tt = t; + + // delay until start of next second + do { + tt = now(); + } while (t == tt); + + ESP_LOGI(TAG, "Sync on Sec %d", second(tt)); + + return second(tt); +} + // interrupt service routine triggered by external interrupt or internal timer void IRAM_ATTR DCF77IRQ() { xTaskNotifyFromISR(DCF77Task, xTaskGetTickCountFromISR(), eSetBits, NULL); diff --git a/src/paxcounter.conf b/src/paxcounter.conf index c796b1d8..1b6d6c3a 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -85,7 +85,8 @@ #define TIME_SYNC_INTERVAL_GPS 60 // sync time each .. minutes from source GPS [default = 60], comment out means off #define TIME_SYNC_INTERVAL_RTC 60 // sync time each .. minutes from RTC [default = 60], comment out means off #define TIME_WRITE_INTERVAL_RTC 60 // write time each .. minutes from GPS/LORA to RTC [default = 60], comment out means off -//#define TIME_SYNC_INTERVAL_LORA 60 // sync time each .. minutes from LORA network [default = 60], comment out means off +//#define TIME_SYNC_INTERVAL_LORA 60 // sync time each .. minutes from LORA network [default = 60], comment out means off +#define TIME_SYNC_INTERVAL_DCF 60 // sync DCF signal time each .. minutes from internal time [default = 60], comment out means off #define IF482_OFFSET 16 // IF482 serial transmit time [ms]: e.g. 9 bits * 17 bytes * 1/9600 bps = 16ms // time zone, see https://github.com/JChristensen/Timezone/blob/master/examples/WorldClock/WorldClock.ino From bad5805810c243328167721b86ce3dd9389ff8eb Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 7 Feb 2019 23:05:26 +0100 Subject: [PATCH 12/23] DCF77 & IF482 improvements --- README.md | 2 +- include/dcf77.h | 6 +--- include/if482.h | 4 +-- include/rtctime.h | 8 +++++ src/dcf77.cpp | 76 +++++++---------------------------------------- src/if482.cpp | 68 +++++++++++++----------------------------- src/rtctime.cpp | 64 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 106 insertions(+), 122 deletions(-) diff --git a/README.md b/README.md index 19085256..77e86dc9 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp # Clock controller -Paxcounter can be used to sync a clock which has DCF77 or IF482 time telegram input with an external time source. Supported external time sources are GPS time, LORAWAN network time (v1.1) or on board RTC time. The precision of the generated DCF77 / IF482 signal depends on precision of used on board time base. Supported are both external time base (e.g. pps pin of GPS chip or oscillator output of RTC chip) and ESP32 internal clock. Selection of time base and clock frequency must be given by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h). +Paxcounter can be used to sync a clock which has DCF77 or IF482 time telegram input with an external time source. Use case of this function is to have paxcounter hardware integrated in clocks, and use it for both counting of pax and controlling the clock. Supported external time sources are GPS time, LORAWAN network time (v1.1) and on board RTC time. Precision of the synthetic DCF77 signal depends on precision of on board available time base. Supported are both external time base (e.g. pps pin of GPS chip or oscillator output of RTC chip) and ESP32 internal clock. Selection of time base and clock frequency is done by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h). # Payload format diff --git a/include/dcf77.h b/include/dcf77.h index eef10541..cd06ea5a 100644 --- a/include/dcf77.h +++ b/include/dcf77.h @@ -2,14 +2,11 @@ #define _DCF77_H #include "globals.h" - -extern TaskHandle_t DCF77Task; -extern hw_timer_t *dcfCycle; +#include "rtctime.h" enum dcf_pulses { dcf_off, dcf_zero, dcf_one }; enum dcf_pinstate { dcf_low, dcf_high }; -void IRAM_ATTR DCF77IRQ(void); int dcf77_init(void); void dcf77_loop(void *pvParameters); void sendDCF77(void); @@ -17,6 +14,5 @@ void DCF_Out(uint8_t startsec); void generateTimeframe(time_t t); void set_DCF77_pin(dcf_pinstate state); uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, uint8_t pArray[]); -uint8_t sync_clock(time_t t); #endif \ No newline at end of file diff --git a/include/if482.h b/include/if482.h index 38a6506b..def3a2fe 100644 --- a/include/if482.h +++ b/include/if482.h @@ -2,11 +2,9 @@ #define _IF482_H #include "globals.h" - -extern TaskHandle_t IF482Task; +#include "rtctime.h" int if482_init(void); void if482_loop(void *pvParameters); -void IRAM_ATTR IF482IRQ(void); #endif \ No newline at end of file diff --git a/include/rtctime.h b/include/rtctime.h index 80f0ca29..479ac557 100644 --- a/include/rtctime.h +++ b/include/rtctime.h @@ -11,11 +11,19 @@ extern RtcDS3231 Rtc; // make RTC instance globally available +extern TaskHandle_t ClockTask; +extern hw_timer_t *clockCycle; + int rtc_init(void); int set_rtctime(uint32_t t); int set_rtctime(time_t t); void sync_rtctime(void); time_t get_rtctime(void); float get_rtctemp(void); +void IRAM_ATTR CLOCKIRQ(); +int pps_init(uint32_t pps_freq); +int pps_init(); +void pps_start(); +uint8_t sync_clock(time_t t); #endif // _RTCTIME_H \ No newline at end of file diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 579b1785..54856953 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -16,9 +16,6 @@ https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/ // Local logging tag static const char TAG[] = "main"; -TaskHandle_t DCF77Task; -hw_timer_t *dcfCycle = NULL; - #define DCF77_FRAME_SIZE (60) #define DCF77_PULSE_DURATION (100) @@ -39,51 +36,19 @@ int dcf77_init(void) { 2048, // stack size of task (void *)1, // parameter of the task 3, // priority of the task - &DCF77Task, // task handle + &ClockTask, // task handle 0); // CPU core - assert(DCF77Task); // has dcf77 task started? + assert(ClockTask); // has clock task started? -#ifdef RTC_INT // if we have hardware pps signal we use it as precise time base - -#ifndef RTC_CLK // assure we know external clock freq -#error "External clock cycle not defined in board hal file" +#if defined RTC_INT && (RTC_CLK == DCF77_PULSE_DURATION) + pps_init(); // use pps clock +#else + pps_init(DCF77_PULSE_DURATION); // use esp32 clock #endif - // setup external interupt for active low RTC INT pin - pinMode(RTC_INT, INPUT_PULLUP); - - // setup external rtc 1Hz clock for triggering DCF77 telegram - ESP_LOGI(TAG, "Time base external clock"); - if (I2C_MUTEX_LOCK()) { - Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); - Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); - I2C_MUTEX_UNLOCK(); - } else { - ESP_LOGE(TAG, "I2c bus busy - RTC initialization error"); - return 0; // failure - } - -#else // if we don't have pps signal from RTC we use ESP32 hardware timer - -#define RTC_CLK (DCF77_PULSE_DURATION) // setup clock cycle - ESP_LOGI(TAG, "Time base ESP32 clock"); - dcfCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec - timerAttachInterrupt(dcfCycle, &DCF77IRQ, true); - timerAlarmWrite(dcfCycle, 10 * RTC_CLK, true); // 100ms - -#endif - - // wait until beginning of next second, then kick off first DCF pulse and - // start clock signal - - DCF_Out(sync_clock(now())); - -#ifdef RTC_INT // start external clock - attachInterrupt(digitalPinToInterrupt(RTC_INT), DCF77IRQ, FALLING); -#else // start internal clock - timerAlarmEnable(dcfCycle); -#endif + DCF_Out(sync_clock(now())); // sync DCF time on next second + pps_start(); // start pulse return 1; // success } // ifdcf77_init @@ -157,9 +122,8 @@ void dcf77_loop(void *pvParameters) { &wakeTime, // receives moment of call from isr portMAX_DELAY); // wait forever (missing error handling here...) -#if (RTC_CLK == DCF77_PULSE_DURATION) - DCF_Out(0); // we don't need clock rescaling - +#if !defined RTC_CLK || (RTC_CLK == DCF77_PULSE_DURATION) // we don't need clock rescaling + DCF_Out(0); #else // we need clock rescaling by software timer for (uint8_t i = 1; i <= RTC_CLK / DCF77_PULSE_DURATION; i++) { DCF_Out(0); @@ -253,24 +217,4 @@ void set_DCF77_pin(dcf_pinstate state) { } // switch } // DCF77_pulse -// helper function to sync phase of DCF output signal to start of second t -uint8_t sync_clock(time_t t) { - time_t tt = t; - - // delay until start of next second - do { - tt = now(); - } while (t == tt); - - ESP_LOGI(TAG, "Sync on Sec %d", second(tt)); - - return second(tt); -} - -// interrupt service routine triggered by external interrupt or internal timer -void IRAM_ATTR DCF77IRQ() { - xTaskNotifyFromISR(DCF77Task, xTaskGetTickCountFromISR(), eSetBits, NULL); - portYIELD_FROM_ISR(); -} - #endif // HAS_DCF77 \ No newline at end of file diff --git a/src/if482.cpp b/src/if482.cpp index 307de066..cfa3ec71 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -1,4 +1,4 @@ -#if defined HAS_IF482 && defined RTC_INT +#if defined HAS_IF482 /* NOTE: The IF482 Generator needs an high precise 1 Hz clock signal which cannot be @@ -84,15 +84,16 @@ not evaluated by model BU-190 // Local logging tag static const char TAG[] = "main"; -TaskHandle_t IF482Task; +#define IF482_FRAME_SIZE (17) +#define IF482_PULSE_DURATION (1000) HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) // initialize and configure IF482 Generator int if482_init(void) { - // setup external interupt for active low RTC INT pin - pinMode(RTC_INT, INPUT_PULLUP); + // open serial interface + IF482.begin(HAS_IF482); // start if482 serial output feed task xTaskCreatePinnedToCore(if482_loop, // task function @@ -100,49 +101,26 @@ int if482_init(void) { 2048, // stack size of task (void *)1, // parameter of the task 3, // priority of the task - &IF482Task, // task handle + &ClockTask, // task handle 0); // CPU core - assert(IF482Task); // has if482loop task started? + assert(ClockTask); // has clock task started? - // open serial interface - IF482.begin(HAS_IF482); - - // if we have hardware pps signal we use it as precise time base -#ifdef RTC_INT -// assure we know clock freq -#ifndef RTC_CLK -#error "No RTC clock cycle defined in board hal file" -#endif - // use external rtc 1Hz clock for triggering IF482 telegram - if (I2C_MUTEX_LOCK()) { - Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); - Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); - I2C_MUTEX_UNLOCK(); - } else { - ESP_LOGE(TAG, "I2c bus busy - IF482 initialization error"); - return 0; // failure - } - attachInterrupt(digitalPinToInterrupt(RTC_INT), IF482IRQ, FALLING); - -// no RTC, thus we use less precise ESP32 hardware timer +#if defined RTC_INT && (RTC_CLK == IF482_PULSE_DURATION) + pps_init(); // use pps clock #else - // setup 1000ms clock signal for IF482 generator using esp32 hardware timer 1 - ESP_LOGD(TAG, "Starting IF482 pulse..."); - dcfCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec - timerAttachInterrupt(dcfCycle, &IF482IRQ, true); - timerAlarmWrite(dcfCycle, 10000, true); // 1000ms cycle - timerAlarmEnable(dcfCycle); + pps_init(IF482_PULSE_DURATION); // use esp32 clock #endif + pps_start(); // start pulse + return 1; // success - } // if482_init String IF482_Out(time_t tt) { time_t t = myTZ.toLocal(tt); - char mon, buf[14], out[17]; + char mon, buf[14], out[IF482_FRAME_SIZE]; switch (timeStatus()) { // indicates if time has been set and recently synced case timeSet: // time is set and is synced @@ -179,12 +157,7 @@ void if482_loop(void *pvParameters) { pdMS_TO_TICKS(IF482_OFFSET); // duration of telegram transmit const TickType_t startTime = xTaskGetTickCount(); // now - // wait until begin of a new second - t = tt = now(); - do { - tt = now(); - } while (t == tt); - + sync_clock(now()); // wait until begin of a new second BitsPending = true; // start blink in display // take timestamp at moment of start of new second @@ -198,17 +171,18 @@ void if482_loop(void *pvParameters) { &wakeTime, // receives moment of call from isr portMAX_DELAY); // wait forever (missing error handling here...) +#if !defined RTC_CLK || (RTC_CLK == IF482_PULSE_DURATION) // we don't need clock rescaling // now we're synced to start of second tt and wait // until it's time to start transmit telegram for tt+1 vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot IF482.print(IF482_Out(now() + 1)); + +#else // we need clock rescaling by software timer + /* + not yet implemented for IF482 + */ +#endif } } // if482_loop() -// interrupt service routine triggered by RTC 1Hz precise clock -void IRAM_ATTR IF482IRQ() { - xTaskNotifyFromISR(IF482Task, xTaskGetTickCountFromISR(), eSetBits, NULL); - portYIELD_FROM_ISR(); -} - #endif // HAS_IF482 \ No newline at end of file diff --git a/src/rtctime.cpp b/src/rtctime.cpp index 4f299c8e..bd2ae503 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -7,6 +7,9 @@ static const char TAG[] = "main"; RtcDS3231 Rtc(Wire); // RTC hardware i2c interface +TaskHandle_t ClockTask; +hw_timer_t *clockCycle = NULL; + // initialize RTC int rtc_init(void) { @@ -97,4 +100,65 @@ float get_rtctemp(void) { return 0; } // get_rtctemp() +int pps_init() { +// we have hardware pps signal as time base +#if defined RTC_INT && defined RTC_CLK + + // setup external interupt for active low RTC INT pin + pinMode(RTC_INT, INPUT_PULLUP); + + // setup external rtc 1Hz clock as pulse per second clock + ESP_LOGI(TAG, "Time base external clock"); + if (I2C_MUTEX_LOCK()) { + Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); + Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); + I2C_MUTEX_UNLOCK(); + } else { + ESP_LOGE(TAG, "I2c bus busy - RTC initialization error"); + return 0; // failure + } + return 1; // success +#endif +} + +int pps_init(uint32_t pps_freq) { + // if we don't have hardware pps we use ESP32 hardware timer + if (pps_freq) { + ESP_LOGI(TAG, "Time base ESP32 clock"); + clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler + timerAttachInterrupt(clockCycle, &CLOCKIRQ, true); + timerAlarmWrite(clockCycle, 10 * pps_freq, true); + } else { + ESP_LOGE(TAG, "Invalid pps clock frequency"); + return 0; // failure + } + return 1; // success +} + +void pps_start() { +#ifdef RTC_INT // start external clock + attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); +#else // start internal clock + timerAlarmEnable(clockCycle); +#endif +} + +// helper function to sync phase of DCF output signal to start of second t +uint8_t sync_clock(time_t t) { + time_t tt = t; + // delay until start of next second + do { + tt = now(); + } while (t == tt); + ESP_LOGI(TAG, "Sync on Sec %d", second(tt)); + return second(tt); +} + +// interrupt service routine triggered by either rtc pps or esp32 hardware +// timer +void IRAM_ATTR CLOCKIRQ() { + xTaskNotifyFromISR(ClockTask, xTaskGetTickCountFromISR(), eSetBits, NULL); + portYIELD_FROM_ISR(); +} + #endif // HAS_RTC \ No newline at end of file From f319d669ec87a93fa15c808aa91b6e4a561ec61f Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 7 Feb 2019 23:11:10 +0100 Subject: [PATCH 13/23] DCF77+IF482 code sanitizations --- src/dcf77.cpp | 4 ---- src/if482.cpp | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 54856953..29182a5f 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -1,9 +1,6 @@ /* // Emulate a DCF77 radio receiver // -// parts of this code werde adapted from source: -https://www.elektormagazine.com/labs/dcf77-emulator-with-esp8266-elektor-labs-version-150713 -// // a nice & free logic test program for DCF77 can be found here: https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/ // @@ -25,7 +22,6 @@ uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; // initialize and configure DCF77 output int dcf77_init(void) { - time_t t, tt; BitsPending = false; pinMode(HAS_DCF77, OUTPUT); diff --git a/src/if482.cpp b/src/if482.cpp index cfa3ec71..da582716 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -152,7 +152,6 @@ void if482_loop(void *pvParameters) { configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check TickType_t wakeTime; - time_t t, tt; const TickType_t timeOffset = pdMS_TO_TICKS(IF482_OFFSET); // duration of telegram transmit const TickType_t startTime = xTaskGetTickCount(); // now @@ -172,8 +171,7 @@ void if482_loop(void *pvParameters) { portMAX_DELAY); // wait forever (missing error handling here...) #if !defined RTC_CLK || (RTC_CLK == IF482_PULSE_DURATION) // we don't need clock rescaling - // now we're synced to start of second tt and wait - // until it's time to start transmit telegram for tt+1 + // wait until it's time to start transmit telegram for next second vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot IF482.print(IF482_Out(now() + 1)); From 160b282c3a411fa37b7ce687b5778f202c93b02b Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Fri, 8 Feb 2019 10:38:53 +0100 Subject: [PATCH 14/23] DCF77 + IF482 code sanitizations --- include/rtctime.h | 1 - src/dcf77.cpp | 22 +++++++++++++--------- src/if482.cpp | 31 +++++++++++++++++-------------- src/main.cpp | 6 ++---- src/rtctime.cpp | 33 ++++++++++++++++----------------- 5 files changed, 48 insertions(+), 45 deletions(-) diff --git a/include/rtctime.h b/include/rtctime.h index 479ac557..5b8672e9 100644 --- a/include/rtctime.h +++ b/include/rtctime.h @@ -22,7 +22,6 @@ time_t get_rtctime(void); float get_rtctemp(void); void IRAM_ATTR CLOCKIRQ(); int pps_init(uint32_t pps_freq); -int pps_init(); void pps_start(); uint8_t sync_clock(time_t t); diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 29182a5f..8cc2313c 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -6,7 +6,11 @@ https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/ // */ -#if defined HAS_DCF77 +#ifdef HAS_DCF77 + +#ifdef IF_482 +#error "You must define at most one of IF482 or DCF_77" +#endif #include "dcf77.h" @@ -15,6 +19,11 @@ static const char TAG[] = "main"; #define DCF77_FRAME_SIZE (60) #define DCF77_PULSE_DURATION (100) +#ifdef RTC_CLK +#define PPS (RTC_CLK / DCF77_PULSE_DURATION) +#else +#define PPS DCF77_PULSE_DURATION +#endif // array of dcf pulses for three minutes uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; @@ -37,12 +46,7 @@ int dcf77_init(void) { assert(ClockTask); // has clock task started? -#if defined RTC_INT && (RTC_CLK == DCF77_PULSE_DURATION) - pps_init(); // use pps clock -#else - pps_init(DCF77_PULSE_DURATION); // use esp32 clock -#endif - + pps_init(PPS); // setup pulse DCF_Out(sync_clock(now())); // sync DCF time on next second pps_start(); // start pulse @@ -118,10 +122,10 @@ void dcf77_loop(void *pvParameters) { &wakeTime, // receives moment of call from isr portMAX_DELAY); // wait forever (missing error handling here...) -#if !defined RTC_CLK || (RTC_CLK == DCF77_PULSE_DURATION) // we don't need clock rescaling +#if (PPS == DCF77_PULSE_DURATION) // we don't need clock rescaling DCF_Out(0); #else // we need clock rescaling by software timer - for (uint8_t i = 1; i <= RTC_CLK / DCF77_PULSE_DURATION; i++) { + for (uint8_t i = 1; i <= PPS; i++) { DCF_Out(0); vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION)); } diff --git a/src/if482.cpp b/src/if482.cpp index da582716..41ac2834 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -1,5 +1,3 @@ -#if defined HAS_IF482 - /* 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 @@ -79,6 +77,12 @@ not evaluated by model BU-190 */ /////////////////////////////////////////////////////////////////////////////// +#ifdef HAS_IF482 + +#ifdef HAS_DCF77 +#error "You must define at most one of IF482 or DCF_77" +#endif + #include "if482.h" // Local logging tag @@ -86,6 +90,11 @@ static const char TAG[] = "main"; #define IF482_FRAME_SIZE (17) #define IF482_PULSE_DURATION (1000) +#ifdef RTC_CLK +#define PPS (RTC_CLK / IF482_PULSE_DURATION) +#else +#define PPS IF482_PULSE_DURATION +#endif HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) @@ -106,13 +115,8 @@ int if482_init(void) { assert(ClockTask); // has clock task started? -#if defined RTC_INT && (RTC_CLK == IF482_PULSE_DURATION) - pps_init(); // use pps clock -#else - pps_init(IF482_PULSE_DURATION); // use esp32 clock -#endif - - pps_start(); // start pulse + pps_init(PPS); // setup pulse + pps_start(); // start pulse return 1; // success } // if482_init @@ -170,15 +174,14 @@ void if482_loop(void *pvParameters) { &wakeTime, // receives moment of call from isr portMAX_DELAY); // wait forever (missing error handling here...) -#if !defined RTC_CLK || (RTC_CLK == IF482_PULSE_DURATION) // we don't need clock rescaling +#if (PPS == DCF77_PULSE_DURATION) // we don't need clock rescaling // wait until it's time to start transmit telegram for next second vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot IF482.print(IF482_Out(now() + 1)); - #else // we need clock rescaling by software timer - /* - not yet implemented for IF482 - */ + /* + not yet implemented for IF482 + */ #endif } } // if482_loop() diff --git a/src/main.cpp b/src/main.cpp index 288c5e61..b2ce3336 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,7 +65,7 @@ char display_line6[16], display_line7[16]; // display buffers uint8_t volatile channel = 0; // channel rotation counter uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, batt_voltage = 0; // globals for display -bool volatile BitsPending = false; // DCF77 or IF482 ticker indicator +bool volatile BitsPending = false; // DCF77 or IF482 ticker indicator hw_timer_t *sendCycle = NULL, *homeCycle = NULL; #ifdef HAS_DISPLAY @@ -416,9 +416,7 @@ void setup() { setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60); #endif -#if (defined HAS_IF482) && (defined DCF_77) -#error "You may define at most one of HAS_IF482 or DCF_77" -#elif defined HAS_IF482 +#ifdef HAS_IF482 ESP_LOGI(TAG, "Starting IF482 Generator..."); assert(if482_init()); #elif defined HAS_DCF77 diff --git a/src/rtctime.cpp b/src/rtctime.cpp index bd2ae503..5c08af77 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -1,15 +1,15 @@ -#ifdef HAS_RTC - #include "rtctime.h" // Local logging tag static const char TAG[] = "main"; -RtcDS3231 Rtc(Wire); // RTC hardware i2c interface - TaskHandle_t ClockTask; hw_timer_t *clockCycle = NULL; +#ifdef HAS_RTC // we have hardware RTC + +RtcDS3231 Rtc(Wire); // RTC hardware i2c interface + // initialize RTC int rtc_init(void) { @@ -100,9 +100,11 @@ float get_rtctemp(void) { return 0; } // get_rtctemp() -int pps_init() { -// we have hardware pps signal as time base -#if defined RTC_INT && defined RTC_CLK +#endif // HAS_RTC + +int pps_init(uint32_t clk_freq_Hz) { +// use fixed pulse clock as time base +#if defined RTC_INT && (RTC_CLK == clk_freq_Hz) // setup external interupt for active low RTC INT pin pinMode(RTC_INT, INPUT_PULLUP); @@ -118,21 +120,20 @@ int pps_init() { return 0; // failure } return 1; // success -#endif -} -int pps_init(uint32_t pps_freq) { - // if we don't have hardware pps we use ESP32 hardware timer - if (pps_freq) { +#else + // use clock with adjustable frequency + if (clk_freq_Hz) { ESP_LOGI(TAG, "Time base ESP32 clock"); clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler timerAttachInterrupt(clockCycle, &CLOCKIRQ, true); - timerAlarmWrite(clockCycle, 10 * pps_freq, true); + timerAlarmWrite(clockCycle, 100 * clk_freq_Hz, true); } else { - ESP_LOGE(TAG, "Invalid pps clock frequency"); + ESP_LOGE(TAG, "Invalid pulse clock frequency"); return 0; // failure } return 1; // success +#endif } void pps_start() { @@ -159,6 +160,4 @@ uint8_t sync_clock(time_t t) { void IRAM_ATTR CLOCKIRQ() { xTaskNotifyFromISR(ClockTask, xTaskGetTickCountFromISR(), eSetBits, NULL); portYIELD_FROM_ISR(); -} - -#endif // HAS_RTC \ No newline at end of file +} \ No newline at end of file From 5fcfdd91e5f70644fa22740f5d7fb8d1c2524342 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Fri, 8 Feb 2019 21:48:56 +0100 Subject: [PATCH 15/23] Revert "DCF77 + IF482 code sanitizations" This reverts commit 160b282c3a411fa37b7ce687b5778f202c93b02b. --- include/rtctime.h | 1 + src/dcf77.cpp | 22 +++++++++------------- src/if482.cpp | 31 ++++++++++++++----------------- src/main.cpp | 6 ++++-- src/rtctime.cpp | 33 +++++++++++++++++---------------- 5 files changed, 45 insertions(+), 48 deletions(-) diff --git a/include/rtctime.h b/include/rtctime.h index 5b8672e9..479ac557 100644 --- a/include/rtctime.h +++ b/include/rtctime.h @@ -22,6 +22,7 @@ time_t get_rtctime(void); float get_rtctemp(void); void IRAM_ATTR CLOCKIRQ(); int pps_init(uint32_t pps_freq); +int pps_init(); void pps_start(); uint8_t sync_clock(time_t t); diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 8cc2313c..29182a5f 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -6,11 +6,7 @@ https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/ // */ -#ifdef HAS_DCF77 - -#ifdef IF_482 -#error "You must define at most one of IF482 or DCF_77" -#endif +#if defined HAS_DCF77 #include "dcf77.h" @@ -19,11 +15,6 @@ static const char TAG[] = "main"; #define DCF77_FRAME_SIZE (60) #define DCF77_PULSE_DURATION (100) -#ifdef RTC_CLK -#define PPS (RTC_CLK / DCF77_PULSE_DURATION) -#else -#define PPS DCF77_PULSE_DURATION -#endif // array of dcf pulses for three minutes uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; @@ -46,7 +37,12 @@ int dcf77_init(void) { assert(ClockTask); // has clock task started? - pps_init(PPS); // setup pulse +#if defined RTC_INT && (RTC_CLK == DCF77_PULSE_DURATION) + pps_init(); // use pps clock +#else + pps_init(DCF77_PULSE_DURATION); // use esp32 clock +#endif + DCF_Out(sync_clock(now())); // sync DCF time on next second pps_start(); // start pulse @@ -122,10 +118,10 @@ void dcf77_loop(void *pvParameters) { &wakeTime, // receives moment of call from isr portMAX_DELAY); // wait forever (missing error handling here...) -#if (PPS == DCF77_PULSE_DURATION) // we don't need clock rescaling +#if !defined RTC_CLK || (RTC_CLK == DCF77_PULSE_DURATION) // we don't need clock rescaling DCF_Out(0); #else // we need clock rescaling by software timer - for (uint8_t i = 1; i <= PPS; i++) { + for (uint8_t i = 1; i <= RTC_CLK / DCF77_PULSE_DURATION; i++) { DCF_Out(0); vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION)); } diff --git a/src/if482.cpp b/src/if482.cpp index 41ac2834..da582716 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -1,3 +1,5 @@ +#if defined HAS_IF482 + /* 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 @@ -77,12 +79,6 @@ not evaluated by model BU-190 */ /////////////////////////////////////////////////////////////////////////////// -#ifdef HAS_IF482 - -#ifdef HAS_DCF77 -#error "You must define at most one of IF482 or DCF_77" -#endif - #include "if482.h" // Local logging tag @@ -90,11 +86,6 @@ static const char TAG[] = "main"; #define IF482_FRAME_SIZE (17) #define IF482_PULSE_DURATION (1000) -#ifdef RTC_CLK -#define PPS (RTC_CLK / IF482_PULSE_DURATION) -#else -#define PPS IF482_PULSE_DURATION -#endif HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) @@ -115,8 +106,13 @@ int if482_init(void) { assert(ClockTask); // has clock task started? - pps_init(PPS); // setup pulse - pps_start(); // start pulse +#if defined RTC_INT && (RTC_CLK == IF482_PULSE_DURATION) + pps_init(); // use pps clock +#else + pps_init(IF482_PULSE_DURATION); // use esp32 clock +#endif + + pps_start(); // start pulse return 1; // success } // if482_init @@ -174,14 +170,15 @@ void if482_loop(void *pvParameters) { &wakeTime, // receives moment of call from isr portMAX_DELAY); // wait forever (missing error handling here...) -#if (PPS == DCF77_PULSE_DURATION) // we don't need clock rescaling +#if !defined RTC_CLK || (RTC_CLK == IF482_PULSE_DURATION) // we don't need clock rescaling // wait until it's time to start transmit telegram for next second vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot IF482.print(IF482_Out(now() + 1)); + #else // we need clock rescaling by software timer - /* - not yet implemented for IF482 - */ + /* + not yet implemented for IF482 + */ #endif } } // if482_loop() diff --git a/src/main.cpp b/src/main.cpp index b2ce3336..288c5e61 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,7 +65,7 @@ char display_line6[16], display_line7[16]; // display buffers uint8_t volatile channel = 0; // channel rotation counter uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, batt_voltage = 0; // globals for display -bool volatile BitsPending = false; // DCF77 or IF482 ticker indicator +bool volatile BitsPending = false; // DCF77 or IF482 ticker indicator hw_timer_t *sendCycle = NULL, *homeCycle = NULL; #ifdef HAS_DISPLAY @@ -416,7 +416,9 @@ void setup() { setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60); #endif -#ifdef HAS_IF482 +#if (defined HAS_IF482) && (defined DCF_77) +#error "You may define at most one of HAS_IF482 or DCF_77" +#elif defined HAS_IF482 ESP_LOGI(TAG, "Starting IF482 Generator..."); assert(if482_init()); #elif defined HAS_DCF77 diff --git a/src/rtctime.cpp b/src/rtctime.cpp index 5c08af77..bd2ae503 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -1,15 +1,15 @@ +#ifdef HAS_RTC + #include "rtctime.h" // Local logging tag static const char TAG[] = "main"; +RtcDS3231 Rtc(Wire); // RTC hardware i2c interface + TaskHandle_t ClockTask; hw_timer_t *clockCycle = NULL; -#ifdef HAS_RTC // we have hardware RTC - -RtcDS3231 Rtc(Wire); // RTC hardware i2c interface - // initialize RTC int rtc_init(void) { @@ -100,11 +100,9 @@ float get_rtctemp(void) { return 0; } // get_rtctemp() -#endif // HAS_RTC - -int pps_init(uint32_t clk_freq_Hz) { -// use fixed pulse clock as time base -#if defined RTC_INT && (RTC_CLK == clk_freq_Hz) +int pps_init() { +// we have hardware pps signal as time base +#if defined RTC_INT && defined RTC_CLK // setup external interupt for active low RTC INT pin pinMode(RTC_INT, INPUT_PULLUP); @@ -120,20 +118,21 @@ int pps_init(uint32_t clk_freq_Hz) { return 0; // failure } return 1; // success +#endif +} -#else - // use clock with adjustable frequency - if (clk_freq_Hz) { +int pps_init(uint32_t pps_freq) { + // if we don't have hardware pps we use ESP32 hardware timer + if (pps_freq) { ESP_LOGI(TAG, "Time base ESP32 clock"); clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler timerAttachInterrupt(clockCycle, &CLOCKIRQ, true); - timerAlarmWrite(clockCycle, 100 * clk_freq_Hz, true); + timerAlarmWrite(clockCycle, 10 * pps_freq, true); } else { - ESP_LOGE(TAG, "Invalid pulse clock frequency"); + ESP_LOGE(TAG, "Invalid pps clock frequency"); return 0; // failure } return 1; // success -#endif } void pps_start() { @@ -160,4 +159,6 @@ uint8_t sync_clock(time_t t) { void IRAM_ATTR CLOCKIRQ() { xTaskNotifyFromISR(ClockTask, xTaskGetTickCountFromISR(), eSetBits, NULL); portYIELD_FROM_ISR(); -} \ No newline at end of file +} + +#endif // HAS_RTC \ No newline at end of file From bfc8f13cc7b475a5471582cbb20a91a2f7761e0f Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Fri, 8 Feb 2019 22:19:44 +0100 Subject: [PATCH 16/23] Revert "Revert "DCF77 + IF482 code sanitizations"" This reverts commit 5fcfdd91e5f70644fa22740f5d7fb8d1c2524342. --- include/rtctime.h | 1 - src/dcf77.cpp | 22 +++++++++++++--------- src/if482.cpp | 31 +++++++++++++++++-------------- src/main.cpp | 6 ++---- src/rtctime.cpp | 33 ++++++++++++++++----------------- 5 files changed, 48 insertions(+), 45 deletions(-) diff --git a/include/rtctime.h b/include/rtctime.h index 479ac557..5b8672e9 100644 --- a/include/rtctime.h +++ b/include/rtctime.h @@ -22,7 +22,6 @@ time_t get_rtctime(void); float get_rtctemp(void); void IRAM_ATTR CLOCKIRQ(); int pps_init(uint32_t pps_freq); -int pps_init(); void pps_start(); uint8_t sync_clock(time_t t); diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 29182a5f..8cc2313c 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -6,7 +6,11 @@ https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/ // */ -#if defined HAS_DCF77 +#ifdef HAS_DCF77 + +#ifdef IF_482 +#error "You must define at most one of IF482 or DCF_77" +#endif #include "dcf77.h" @@ -15,6 +19,11 @@ static const char TAG[] = "main"; #define DCF77_FRAME_SIZE (60) #define DCF77_PULSE_DURATION (100) +#ifdef RTC_CLK +#define PPS (RTC_CLK / DCF77_PULSE_DURATION) +#else +#define PPS DCF77_PULSE_DURATION +#endif // array of dcf pulses for three minutes uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; @@ -37,12 +46,7 @@ int dcf77_init(void) { assert(ClockTask); // has clock task started? -#if defined RTC_INT && (RTC_CLK == DCF77_PULSE_DURATION) - pps_init(); // use pps clock -#else - pps_init(DCF77_PULSE_DURATION); // use esp32 clock -#endif - + pps_init(PPS); // setup pulse DCF_Out(sync_clock(now())); // sync DCF time on next second pps_start(); // start pulse @@ -118,10 +122,10 @@ void dcf77_loop(void *pvParameters) { &wakeTime, // receives moment of call from isr portMAX_DELAY); // wait forever (missing error handling here...) -#if !defined RTC_CLK || (RTC_CLK == DCF77_PULSE_DURATION) // we don't need clock rescaling +#if (PPS == DCF77_PULSE_DURATION) // we don't need clock rescaling DCF_Out(0); #else // we need clock rescaling by software timer - for (uint8_t i = 1; i <= RTC_CLK / DCF77_PULSE_DURATION; i++) { + for (uint8_t i = 1; i <= PPS; i++) { DCF_Out(0); vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION)); } diff --git a/src/if482.cpp b/src/if482.cpp index da582716..41ac2834 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -1,5 +1,3 @@ -#if defined HAS_IF482 - /* 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 @@ -79,6 +77,12 @@ not evaluated by model BU-190 */ /////////////////////////////////////////////////////////////////////////////// +#ifdef HAS_IF482 + +#ifdef HAS_DCF77 +#error "You must define at most one of IF482 or DCF_77" +#endif + #include "if482.h" // Local logging tag @@ -86,6 +90,11 @@ static const char TAG[] = "main"; #define IF482_FRAME_SIZE (17) #define IF482_PULSE_DURATION (1000) +#ifdef RTC_CLK +#define PPS (RTC_CLK / IF482_PULSE_DURATION) +#else +#define PPS IF482_PULSE_DURATION +#endif HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) @@ -106,13 +115,8 @@ int if482_init(void) { assert(ClockTask); // has clock task started? -#if defined RTC_INT && (RTC_CLK == IF482_PULSE_DURATION) - pps_init(); // use pps clock -#else - pps_init(IF482_PULSE_DURATION); // use esp32 clock -#endif - - pps_start(); // start pulse + pps_init(PPS); // setup pulse + pps_start(); // start pulse return 1; // success } // if482_init @@ -170,15 +174,14 @@ void if482_loop(void *pvParameters) { &wakeTime, // receives moment of call from isr portMAX_DELAY); // wait forever (missing error handling here...) -#if !defined RTC_CLK || (RTC_CLK == IF482_PULSE_DURATION) // we don't need clock rescaling +#if (PPS == DCF77_PULSE_DURATION) // we don't need clock rescaling // wait until it's time to start transmit telegram for next second vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot IF482.print(IF482_Out(now() + 1)); - #else // we need clock rescaling by software timer - /* - not yet implemented for IF482 - */ + /* + not yet implemented for IF482 + */ #endif } } // if482_loop() diff --git a/src/main.cpp b/src/main.cpp index 288c5e61..b2ce3336 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,7 +65,7 @@ char display_line6[16], display_line7[16]; // display buffers uint8_t volatile channel = 0; // channel rotation counter uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, batt_voltage = 0; // globals for display -bool volatile BitsPending = false; // DCF77 or IF482 ticker indicator +bool volatile BitsPending = false; // DCF77 or IF482 ticker indicator hw_timer_t *sendCycle = NULL, *homeCycle = NULL; #ifdef HAS_DISPLAY @@ -416,9 +416,7 @@ void setup() { setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60); #endif -#if (defined HAS_IF482) && (defined DCF_77) -#error "You may define at most one of HAS_IF482 or DCF_77" -#elif defined HAS_IF482 +#ifdef HAS_IF482 ESP_LOGI(TAG, "Starting IF482 Generator..."); assert(if482_init()); #elif defined HAS_DCF77 diff --git a/src/rtctime.cpp b/src/rtctime.cpp index bd2ae503..5c08af77 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -1,15 +1,15 @@ -#ifdef HAS_RTC - #include "rtctime.h" // Local logging tag static const char TAG[] = "main"; -RtcDS3231 Rtc(Wire); // RTC hardware i2c interface - TaskHandle_t ClockTask; hw_timer_t *clockCycle = NULL; +#ifdef HAS_RTC // we have hardware RTC + +RtcDS3231 Rtc(Wire); // RTC hardware i2c interface + // initialize RTC int rtc_init(void) { @@ -100,9 +100,11 @@ float get_rtctemp(void) { return 0; } // get_rtctemp() -int pps_init() { -// we have hardware pps signal as time base -#if defined RTC_INT && defined RTC_CLK +#endif // HAS_RTC + +int pps_init(uint32_t clk_freq_Hz) { +// use fixed pulse clock as time base +#if defined RTC_INT && (RTC_CLK == clk_freq_Hz) // setup external interupt for active low RTC INT pin pinMode(RTC_INT, INPUT_PULLUP); @@ -118,21 +120,20 @@ int pps_init() { return 0; // failure } return 1; // success -#endif -} -int pps_init(uint32_t pps_freq) { - // if we don't have hardware pps we use ESP32 hardware timer - if (pps_freq) { +#else + // use clock with adjustable frequency + if (clk_freq_Hz) { ESP_LOGI(TAG, "Time base ESP32 clock"); clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler timerAttachInterrupt(clockCycle, &CLOCKIRQ, true); - timerAlarmWrite(clockCycle, 10 * pps_freq, true); + timerAlarmWrite(clockCycle, 100 * clk_freq_Hz, true); } else { - ESP_LOGE(TAG, "Invalid pps clock frequency"); + ESP_LOGE(TAG, "Invalid pulse clock frequency"); return 0; // failure } return 1; // success +#endif } void pps_start() { @@ -159,6 +160,4 @@ uint8_t sync_clock(time_t t) { void IRAM_ATTR CLOCKIRQ() { xTaskNotifyFromISR(ClockTask, xTaskGetTickCountFromISR(), eSetBits, NULL); portYIELD_FROM_ISR(); -} - -#endif // HAS_RTC \ No newline at end of file +} \ No newline at end of file From ed5c7f46f3aded21b01423b285ad35f3020054ce Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Fri, 8 Feb 2019 23:08:14 +0100 Subject: [PATCH 17/23] DCF77 + IF482 debugging --- src/cyclic.cpp | 4 ++-- src/dcf77.cpp | 26 +++++++++++++++++--------- src/if482.cpp | 21 ++++++++++++++------- src/rtctime.cpp | 10 +++++----- 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/cyclic.cpp b/src/cyclic.cpp index b4e6e9ff..4b921e5a 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -61,8 +61,8 @@ void doHousekeeping() { uxTaskGetStackHighWaterMark(BmeTask), eTaskGetState(BmeTask)); #endif #ifdef HAS_DCF77 - ESP_LOGD(TAG, "DCF77loop %d bytes left | Taskstate = %d", - uxTaskGetStackHighWaterMark(DCF77Task), eTaskGetState(DCF77Task)); + ESP_LOGD(TAG, "Clockloop %d bytes left | Taskstate = %d", + uxTaskGetStackHighWaterMark(ClockTask), eTaskGetState(ClockTask)); #endif #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 8cc2313c..0648d854 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -9,7 +9,7 @@ https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/ #ifdef HAS_DCF77 #ifdef IF_482 -#error "You must define at most one of IF482 or DCF_77" +#error "You must define at most one of IF482 or DCF77" #endif #include "dcf77.h" @@ -19,8 +19,9 @@ static const char TAG[] = "main"; #define DCF77_FRAME_SIZE (60) #define DCF77_PULSE_DURATION (100) -#ifdef RTC_CLK -#define PPS (RTC_CLK / DCF77_PULSE_DURATION) + +#if defined RTC_INT && defined RTC_CLK +#define PPS RTC_CLK #else #define PPS DCF77_PULSE_DURATION #endif @@ -63,10 +64,14 @@ void DCF_Out(uint8_t startOffset) { #endif if (!BitsPending) { - // prepare frame to send for next minute - generateTimeframe(now() + DCF77_FRAME_SIZE + 1); - // start blinking symbol on display and kick off timer - BitsPending = true; + // do we have confident time/date? + if ((timeStatus() == timeSet) || (timeStatus() == timeNeedsSync)) { + // prepare frame to send for next minute + generateTimeframe(now() + DCF77_FRAME_SIZE + 1); + // start blinking symbol on display and kick off timer + BitsPending = true; + } else + return; } // ticker out current DCF frame @@ -124,11 +129,14 @@ void dcf77_loop(void *pvParameters) { #if (PPS == DCF77_PULSE_DURATION) // we don't need clock rescaling DCF_Out(0); -#else // we need clock rescaling by software timer - for (uint8_t i = 1; i <= PPS; i++) { +#elif (PPS > DCF77_PULSE_DURATION) // we need upclocking + for (uint8_t i = 1; i <= PPS / DCF77_PULSE_DURATION; i++) { DCF_Out(0); vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION)); } +#elif (PPS < DCF77_PULSE_DURATION) // we need downclocking + vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION - PPS)); + DCF_Out(0); #endif } // for } // dcf77_loop() diff --git a/src/if482.cpp b/src/if482.cpp index 41ac2834..71df7a56 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -80,7 +80,7 @@ not evaluated by model BU-190 #ifdef HAS_IF482 #ifdef HAS_DCF77 -#error "You must define at most one of IF482 or DCF_77" +#error "You must define at most one of IF482 or DCF77" #endif #include "if482.h" @@ -90,8 +90,9 @@ static const char TAG[] = "main"; #define IF482_FRAME_SIZE (17) #define IF482_PULSE_DURATION (1000) + #ifdef RTC_CLK -#define PPS (RTC_CLK / IF482_PULSE_DURATION) +#define PPS RTC_CLK #else #define PPS IF482_PULSE_DURATION #endif @@ -174,14 +175,20 @@ void if482_loop(void *pvParameters) { &wakeTime, // receives moment of call from isr portMAX_DELAY); // wait forever (missing error handling here...) -#if (PPS == DCF77_PULSE_DURATION) // we don't need clock rescaling +#if (PPS == IF482_PULSE_DURATION) // we don't need clock rescaling // wait until it's time to start transmit telegram for next second vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot IF482.print(IF482_Out(now() + 1)); -#else // we need clock rescaling by software timer - /* - not yet implemented for IF482 - */ +#elif (PPS > IF482_PULSE_DURATION) // we need upclocking + for (uint8_t i = 1; i <= PPS / IF482_PULSE_DURATION; i++) { + vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot + IF482.print(IF482_Out(now() + 1)); + } +#elif (PPS < IF482_PULSE_DURATION) // we need downclocking + IF482.print(IF482_Out(now() + 1)); + vTaskDelayUntil(&wakeTime, + shotTime - PPS); // sets waketime to moment of shot + #endif } } // if482_loop() diff --git a/src/rtctime.cpp b/src/rtctime.cpp index 5c08af77..de909866 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -102,9 +102,9 @@ float get_rtctemp(void) { #endif // HAS_RTC -int pps_init(uint32_t clk_freq_Hz) { +int pps_init(uint32_t clk_freq_ms) { // use fixed pulse clock as time base -#if defined RTC_INT && (RTC_CLK == clk_freq_Hz) +#if defined RTC_INT && defined RTC_CLK // setup external interupt for active low RTC INT pin pinMode(RTC_INT, INPUT_PULLUP); @@ -123,11 +123,11 @@ int pps_init(uint32_t clk_freq_Hz) { #else // use clock with adjustable frequency - if (clk_freq_Hz) { + if (clk_freq_ms) { ESP_LOGI(TAG, "Time base ESP32 clock"); - clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler + clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec timerAttachInterrupt(clockCycle, &CLOCKIRQ, true); - timerAlarmWrite(clockCycle, 100 * clk_freq_Hz, true); + timerAlarmWrite(clockCycle, 10 * clk_freq_ms, true); //ms } else { ESP_LOGE(TAG, "Invalid pulse clock frequency"); return 0; // failure From fde157dd0d2c8321c1c8d620053265c563709cc0 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 9 Feb 2019 13:02:38 +0100 Subject: [PATCH 18/23] preparations for changing gps library --- include/gpsread.h | 1 + platformio.ini | 6 ++++-- src/dcf77.cpp | 6 ++++++ src/gpsread.cpp | 24 ++++++++++++++++++++++-- src/hal/ttgobeam.h | 12 +++++++++++- src/hal/ttgofox.h | 11 ++++++++--- src/if482.cpp | 10 ++++++++-- src/rtctime.cpp | 20 +++++++++++++------- 8 files changed, 73 insertions(+), 17 deletions(-) diff --git a/include/gpsread.h b/include/gpsread.h index 3cd4a931..9fccefb4 100644 --- a/include/gpsread.h +++ b/include/gpsread.h @@ -16,5 +16,6 @@ int gps_init(void); void gps_read(void); void gps_loop(void *pvParameters); time_t get_gpstime(void); +int gps_config(); #endif \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index dc5b8873..e28b6047 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,8 +15,8 @@ ;env_default = ttgov2 ;env_default = ttgov21old ;env_default = ttgov21new -env_default = ttgobeam -;env_default = ttgofox +;env_default = ttgobeam +env_default = ttgofox ;env_default = lopy ;env_default = lopy4 ;env_default = fipy @@ -50,6 +50,7 @@ lib_deps_rgbled = SmartLeds@>=1.1.5 lib_deps_gps = TinyGPSPlus@>=1.0.2 +; NeoGPS^4.2.9 lib_deps_rtc = RTC@^2.3.0 lib_deps_basic = @@ -222,6 +223,7 @@ build_flags = upload_protocol = ${common.upload_protocol} extra_scripts = ${common.extra_scripts} monitor_speed = ${common.monitor_speed} +upload_port = COM61 [env:ttgobeam] platform = ${common.platform_espressif32} diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 0648d854..a4b1c62a 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -20,8 +20,11 @@ static const char TAG[] = "main"; #define DCF77_FRAME_SIZE (60) #define DCF77_PULSE_DURATION (100) +// select internal / external clock #if defined RTC_INT && defined RTC_CLK #define PPS RTC_CLK +#elif defined GPS_INT && defined GPS_CLK +#define PPS GPS_CLK #else #define PPS DCF77_PULSE_DURATION #endif @@ -127,13 +130,16 @@ void dcf77_loop(void *pvParameters) { &wakeTime, // receives moment of call from isr portMAX_DELAY); // wait forever (missing error handling here...) +// select clock scale #if (PPS == DCF77_PULSE_DURATION) // we don't need clock rescaling DCF_Out(0); + #elif (PPS > DCF77_PULSE_DURATION) // we need upclocking for (uint8_t i = 1; i <= PPS / DCF77_PULSE_DURATION; i++) { DCF_Out(0); vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION)); } + #elif (PPS < DCF77_PULSE_DURATION) // we need downclocking vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION - PPS)); DCF_Out(0); diff --git a/src/gpsread.cpp b/src/gpsread.cpp index a446118e..6e094faf 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -18,6 +18,11 @@ int gps_init(void) { int ret = 1; + if (!gps_config()) { + ESP_LOGE(TAG, "GPS chip initializiation error"); + return 0; + } + #if defined GPS_SERIAL GPS_Serial.begin(GPS_SERIAL); ESP_LOGI(TAG, "Using serial GPS"); @@ -41,6 +46,21 @@ int gps_init(void) { return ret; } // gps_init() +// detect gps chipset type and configure it with device specific settings +int gps_config() { + int rslt = 1; // success +#if defined GPS_SERIAL + + /* to come */ + +#elif defined GPS_I2C + + /* to come */ + +#endif + return rslt; +} + // read GPS data and cast to global struct void gps_read() { gps_status.latitude = (int32_t)(gps.location.lat() * 1e6); @@ -73,8 +93,8 @@ time_t get_gpstime(void) { if ((gps.time.age() < 1500) && (gps.time.isValid())) { t = tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second()); - ESP_LOGD(TAG, "GPS time: %4d/%02d/%02d %02d:%02d:%02d", year(t), month(t), day(t), - hour(t), minute(t), second(t)); + ESP_LOGD(TAG, "GPS time: %4d/%02d/%02d %02d:%02d:%02d", year(t), month(t), + day(t), hour(t), minute(t), second(t)); } else { ESP_LOGW(TAG, "GPS has no confident time"); } diff --git a/src/hal/ttgobeam.h b/src/hal/ttgobeam.h index f49eb27e..c32994a8 100644 --- a/src/hal/ttgobeam.h +++ b/src/hal/ttgobeam.h @@ -19,8 +19,14 @@ #define HAS_BUTTON GPIO_NUM_39 // on board button (next to reset) #define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 #define BATT_FACTOR 2 // voltage divider 100k/100k on board + +// GPS settings #define HAS_GPS 1 // use on board GPS #define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M or 7M with default configuration +// to use +#define GPS_INT GPIO_NUM_34 // 30ns accurary timepulse, to be external wired on pcb: R34 -> GPIO34 +// 1 pulse per second, synchronized at rising edge, pulse length 100ms, accuracy +/- 3 *e-8 [nanoseconds] = 0,95sec / year +#define GPS_CLK (1000) // enable only if device has these sensors, otherwise comment these lines // BME680 sensor on I2C bus @@ -35,7 +41,11 @@ //#define DISPLAY_FLIP 1 // use if display is rotated // Settings for DCF77 interface -#define HAS_DCF77 GPIO_NUM_13 +//#define HAS_DCF77 GPIO_NUM_13 + +// Settings for IF482 interface +#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters + // user defined sensors (if connected) //#define HAS_SENSORS 1 // comment out if device has user defined sensors diff --git a/src/hal/ttgofox.h b/src/hal/ttgofox.h index f7cd3033..7be3544c 100644 --- a/src/hal/ttgofox.h +++ b/src/hal/ttgofox.h @@ -20,12 +20,17 @@ #define MY_OLED_SCL (22) #define MY_OLED_RST U8X8_PIN_NONE -// Pins for on board DS3231 RTC chip +// Settings for on board DS3231 RTC chip #define HAS_RTC MY_OLED_SDA, MY_OLED_SCL // SDA, SCL -#define RTC_INT GPIO_NUM_34 // interrupt input from rtc +//#define RTC_INT GPIO_NUM_34 // timepulse with accuracy +/- 2*e-6 [microseconds] = 0,1728sec / day +//#define RTC_CLK (1000) // frequency of RTC clock signal in ms // Settings for IF482 interface -#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters +//#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters + +// Settings for DCF77 interface +#define HAS_DCF77 GPIO_NUM_14 +#define DCF77_ACTIVE_LOW 1 // Settings for external GPS chip #define HAS_GPS 1 // use on board GPS diff --git a/src/if482.cpp b/src/if482.cpp index 71df7a56..600e3fd5 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -91,12 +91,16 @@ static const char TAG[] = "main"; #define IF482_FRAME_SIZE (17) #define IF482_PULSE_DURATION (1000) -#ifdef RTC_CLK +// select internal / external clock +#if defined RTC_INT && defined RTC_CLK #define PPS RTC_CLK +#elif defined GPS_INT && defined GPS_CLK +#define PPS GPS_CLK #else #define PPS IF482_PULSE_DURATION #endif + HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) // initialize and configure IF482 Generator @@ -175,20 +179,22 @@ void if482_loop(void *pvParameters) { &wakeTime, // receives moment of call from isr portMAX_DELAY); // wait forever (missing error handling here...) +// select clock scale #if (PPS == IF482_PULSE_DURATION) // we don't need clock rescaling // wait until it's time to start transmit telegram for next second vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot IF482.print(IF482_Out(now() + 1)); + #elif (PPS > IF482_PULSE_DURATION) // we need upclocking for (uint8_t i = 1; i <= PPS / IF482_PULSE_DURATION; i++) { vTaskDelayUntil(&wakeTime, shotTime); // sets waketime to moment of shot IF482.print(IF482_Out(now() + 1)); } + #elif (PPS < IF482_PULSE_DURATION) // we need downclocking IF482.print(IF482_Out(now() + 1)); vTaskDelayUntil(&wakeTime, shotTime - PPS); // sets waketime to moment of shot - #endif } } // if482_loop() diff --git a/src/rtctime.cpp b/src/rtctime.cpp index de909866..aee26515 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -102,8 +102,10 @@ float get_rtctemp(void) { #endif // HAS_RTC -int pps_init(uint32_t clk_freq_ms) { -// use fixed pulse clock as time base +// helper function to setup a pulse for time synchronisation +int pps_init(uint32_t pulse_period_ms) { + +// use pulse from on board RTC chip as time base with fixed frequency #if defined RTC_INT && defined RTC_CLK // setup external interupt for active low RTC INT pin @@ -121,13 +123,16 @@ int pps_init(uint32_t clk_freq_ms) { } return 1; // success + #elif defined RTC_INT && defined HAS_GPS + #else - // use clock with adjustable frequency - if (clk_freq_ms) { + // use ESP32 hardware timer as time base with adjustable frequency + if (pulse_period_ms) { ESP_LOGI(TAG, "Time base ESP32 clock"); - clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec + clockCycle = + timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec timerAttachInterrupt(clockCycle, &CLOCKIRQ, true); - timerAlarmWrite(clockCycle, 10 * clk_freq_ms, true); //ms + timerAlarmWrite(clockCycle, 10 * pulse_period_ms, true); // ms } else { ESP_LOGE(TAG, "Invalid pulse clock frequency"); return 0; // failure @@ -138,7 +143,8 @@ int pps_init(uint32_t clk_freq_ms) { void pps_start() { #ifdef RTC_INT // start external clock - attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); + //attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); + attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, RISING); #else // start internal clock timerAlarmEnable(clockCycle); #endif From a46b16aa5d86d1df13e795bb1ab98da317e548b2 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 9 Feb 2019 14:18:46 +0100 Subject: [PATCH 19/23] use GPS timepulse line --- src/rtctime.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/rtctime.cpp b/src/rtctime.cpp index aee26515..feb31406 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -105,8 +105,18 @@ float get_rtctemp(void) { // helper function to setup a pulse for time synchronisation int pps_init(uint32_t pulse_period_ms) { +// use time pulse from GPS as time base with fixed 1Hz frequency +#if defined GPS_INT && defined GPS_CLK + +// setup external interupt for active low RTC INT pin + pinMode(GPS_INT, INPUT_PULLDOWN); + + // setup external rtc 1Hz clock as pulse per second clock + ESP_LOGI(TAG, "Time base GPS timepulse"); + return 1; // success + // use pulse from on board RTC chip as time base with fixed frequency -#if defined RTC_INT && defined RTC_CLK +#elif defined RTC_INT && defined RTC_CLK // setup external interupt for active low RTC INT pin pinMode(RTC_INT, INPUT_PULLUP); @@ -123,9 +133,7 @@ int pps_init(uint32_t pulse_period_ms) { } return 1; // success - #elif defined RTC_INT && defined HAS_GPS - -#else + #else // use ESP32 hardware timer as time base with adjustable frequency if (pulse_period_ms) { ESP_LOGI(TAG, "Time base ESP32 clock"); @@ -138,13 +146,15 @@ int pps_init(uint32_t pulse_period_ms) { return 0; // failure } return 1; // success + #endif } void pps_start() { -#ifdef RTC_INT // start external clock - //attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); - attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, RISING); +#ifdef GPS_INT // start external clock + attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING); +#elif defined RTC_INT // start external clock + attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); #else // start internal clock timerAlarmEnable(clockCycle); #endif From 46f41d0ee843bc1d8370c99cbeb44c50b53c6a73 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 9 Feb 2019 14:21:09 +0100 Subject: [PATCH 20/23] time code sanitizations --- include/rtctime.h | 4 ++-- platformio.ini | 7 +++---- src/dcf77.cpp | 4 ++-- src/if482.cpp | 4 ++-- src/rtctime.cpp | 4 ++-- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/include/rtctime.h b/include/rtctime.h index 5b8672e9..8a98f241 100644 --- a/include/rtctime.h +++ b/include/rtctime.h @@ -21,8 +21,8 @@ void sync_rtctime(void); time_t get_rtctime(void); float get_rtctemp(void); void IRAM_ATTR CLOCKIRQ(); -int pps_init(uint32_t pps_freq); -void pps_start(); +int timepulse_init(uint32_t pps_freq); +void timepulse_start(); uint8_t sync_clock(time_t t); #endif // _RTCTIME_H \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index e28b6047..cf7f1ce3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,8 +15,8 @@ ;env_default = ttgov2 ;env_default = ttgov21old ;env_default = ttgov21new -;env_default = ttgobeam -env_default = ttgofox +env_default = ttgobeam +;env_default = ttgofox ;env_default = lopy ;env_default = lopy4 ;env_default = fipy @@ -223,7 +223,6 @@ build_flags = upload_protocol = ${common.upload_protocol} extra_scripts = ${common.extra_scripts} monitor_speed = ${common.monitor_speed} -upload_port = COM61 [env:ttgobeam] platform = ${common.platform_espressif32} @@ -235,7 +234,7 @@ lib_deps = ${common.lib_deps_basic} ${common.lib_deps_lora} ${common.lib_deps_gps} -; ${common.lib_deps_display} + ${common.lib_deps_display} build_flags = ${common.build_flags_all} -mfix-esp32-psram-cache-issue diff --git a/src/dcf77.cpp b/src/dcf77.cpp index a4b1c62a..ac76948a 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -50,9 +50,9 @@ int dcf77_init(void) { assert(ClockTask); // has clock task started? - pps_init(PPS); // setup pulse + timepulse_init(PPS); // setup pulse DCF_Out(sync_clock(now())); // sync DCF time on next second - pps_start(); // start pulse + timepulse_start(); // start pulse return 1; // success } // ifdcf77_init diff --git a/src/if482.cpp b/src/if482.cpp index 600e3fd5..759d863e 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -120,8 +120,8 @@ int if482_init(void) { assert(ClockTask); // has clock task started? - pps_init(PPS); // setup pulse - pps_start(); // start pulse + timepulse_init(PPS); // setup pulse + timepulse_start(); // start pulse return 1; // success } // if482_init diff --git a/src/rtctime.cpp b/src/rtctime.cpp index feb31406..844d0c82 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -103,7 +103,7 @@ float get_rtctemp(void) { #endif // HAS_RTC // helper function to setup a pulse for time synchronisation -int pps_init(uint32_t pulse_period_ms) { +int timepulse_init(uint32_t pulse_period_ms) { // use time pulse from GPS as time base with fixed 1Hz frequency #if defined GPS_INT && defined GPS_CLK @@ -150,7 +150,7 @@ int pps_init(uint32_t pulse_period_ms) { #endif } -void pps_start() { +void timepulse_start() { #ifdef GPS_INT // start external clock attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING); #elif defined RTC_INT // start external clock From e7a5ab3ca619bbeb3c27c4957cf237028303b475 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 9 Feb 2019 14:46:41 +0100 Subject: [PATCH 21/23] rtctime.cpp: timepulse fixes --- src/rtctime.cpp | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/rtctime.cpp b/src/rtctime.cpp index 844d0c82..d59f574b 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -108,11 +108,16 @@ int timepulse_init(uint32_t pulse_period_ms) { // use time pulse from GPS as time base with fixed 1Hz frequency #if defined GPS_INT && defined GPS_CLK -// setup external interupt for active low RTC INT pin + // setup external interupt for active low RTC INT pin pinMode(GPS_INT, INPUT_PULLDOWN); - // setup external rtc 1Hz clock as pulse per second clock - ESP_LOGI(TAG, "Time base GPS timepulse"); + ESP_LOGI(TAG, "Time base: GPS timepulse"); + switch (GPS_CLK) { + case 1000: + break; // default GPS timepulse 1000ms + default: + goto pulse_period_error; + } return 1; // success // use pulse from on board RTC chip as time base with fixed frequency @@ -120,11 +125,19 @@ int timepulse_init(uint32_t pulse_period_ms) { // setup external interupt for active low RTC INT pin pinMode(RTC_INT, INPUT_PULLUP); - // setup external rtc 1Hz clock as pulse per second clock - ESP_LOGI(TAG, "Time base external clock"); + ESP_LOGI(TAG, "Time base: external RTC timepulse"); if (I2C_MUTEX_LOCK()) { - Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); + switch (RTC_CLK) { + case 1000: // 1000ms + Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); + break; + case 1: // 1ms + Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1kHz); + break; + default: + goto pulse_period_error; + } Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); I2C_MUTEX_UNLOCK(); } else { @@ -133,21 +146,23 @@ int timepulse_init(uint32_t pulse_period_ms) { } return 1; // success - #else +#else // use ESP32 hardware timer as time base with adjustable frequency if (pulse_period_ms) { - ESP_LOGI(TAG, "Time base ESP32 clock"); + ESP_LOGI(TAG, "Time base: ESP32 hardware timer"); clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec timerAttachInterrupt(clockCycle, &CLOCKIRQ, true); timerAlarmWrite(clockCycle, 10 * pulse_period_ms, true); // ms - } else { - ESP_LOGE(TAG, "Invalid pulse clock frequency"); - return 0; // failure - } + } else + goto pulse_period_error; return 1; // success #endif + +pulse_period_error: + ESP_LOGE(TAG, "Unknown timepulse period value"); + return 0; // failure } void timepulse_start() { @@ -155,7 +170,7 @@ void timepulse_start() { attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING); #elif defined RTC_INT // start external clock attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); -#else // start internal clock +#else // start internal clock timerAlarmEnable(clockCycle); #endif } From bac9540d521d8fbc1fab9b64c4adfb1401d009c4 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 9 Feb 2019 15:46:44 +0100 Subject: [PATCH 22/23] code sanitizations --- include/payload.h | 2 +- src/dcf77.cpp | 11 +++++------ src/if482.cpp | 9 +++------ src/payload.cpp | 2 +- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/include/payload.h b/include/payload.h index 24b5ece6..686a5b62 100644 --- a/include/payload.h +++ b/include/payload.h @@ -82,7 +82,7 @@ private: uint8_t cursor; #else -#error "No valid payload converter defined" +#error No valid payload converter defined! #endif }; diff --git a/src/dcf77.cpp b/src/dcf77.cpp index ac76948a..8be4ed61 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -9,7 +9,7 @@ https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/ #ifdef HAS_DCF77 #ifdef IF_482 -#error "You must define at most one of IF482 or DCF77" +#error You must define at most one of IF482 or DCF77! #endif #include "dcf77.h" @@ -50,9 +50,9 @@ int dcf77_init(void) { assert(ClockTask); // has clock task started? - timepulse_init(PPS); // setup pulse + timepulse_init(PPS); // setup pulse DCF_Out(sync_clock(now())); // sync DCF time on next second - timepulse_start(); // start pulse + timepulse_start(); // start pulse return 1; // success } // ifdcf77_init @@ -140,9 +140,8 @@ void dcf77_loop(void *pvParameters) { vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION)); } -#elif (PPS < DCF77_PULSE_DURATION) // we need downclocking - vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION - PPS)); - DCF_Out(0); +#elif (PPS < DCF77_PULSE_DURATION) // we need downclocking, not yet implemented +#error Timepulse is too low for DCF77! #endif } // for } // dcf77_loop() diff --git a/src/if482.cpp b/src/if482.cpp index 759d863e..97aff8c4 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -80,7 +80,7 @@ not evaluated by model BU-190 #ifdef HAS_IF482 #ifdef HAS_DCF77 -#error "You must define at most one of IF482 or DCF77" +#error You must define at most one of IF482 or DCF77! #endif #include "if482.h" @@ -100,7 +100,6 @@ static const char TAG[] = "main"; #define PPS IF482_PULSE_DURATION #endif - HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) // initialize and configure IF482 Generator @@ -191,10 +190,8 @@ void if482_loop(void *pvParameters) { IF482.print(IF482_Out(now() + 1)); } -#elif (PPS < IF482_PULSE_DURATION) // we need downclocking - IF482.print(IF482_Out(now() + 1)); - vTaskDelayUntil(&wakeTime, - shotTime - PPS); // sets waketime to moment of shot +#elif (PPS < IF482_PULSE_DURATION) // we need downclocking, not yet implemented +#error Timepulse is too low for IF482! #endif } } // if482_loop() diff --git a/src/payload.cpp b/src/payload.cpp index 71534a24..eebe88d7 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -437,5 +437,5 @@ void PayloadConvert::addButton(uint8_t value) { } #else -#error "No valid payload converter defined" +#error No valid payload converter defined! #endif \ No newline at end of file From 70fb3ecd4552b6dcab94871882cc411b5d0fe0b9 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 9 Feb 2019 16:10:21 +0100 Subject: [PATCH 23/23] v1.7.2 --- README.md | 2 +- platformio.ini | 8 ++++---- src/hal/generic.h | 17 ++++++++++------- src/hal/ttgobeam.h | 23 ++++++++++------------- src/hal/ttgofox.h | 10 +++++----- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 77e86dc9..e44aafc1 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp # Clock controller -Paxcounter can be used to sync a clock which has DCF77 or IF482 time telegram input with an external time source. Use case of this function is to have paxcounter hardware integrated in clocks, and use it for both counting of pax and controlling the clock. Supported external time sources are GPS time, LORAWAN network time (v1.1) and on board RTC time. Precision of the synthetic DCF77 signal depends on precision of on board available time base. Supported are both external time base (e.g. pps pin of GPS chip or oscillator output of RTC chip) and ESP32 internal clock. Selection of time base and clock frequency is done by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h). +Paxcounter can be used to sync a clock which has DCF77 or IF482 time telegram input with an external time source. Use case of this function is to have paxcounter hardware integrated in clocks, and use it for both counting of pax and controlling the clock. Supported external time sources are GPS time, LORAWAN network time (v1.1) and on board RTC time. Precision of the synthetic DCF77 signal depends on precision of on board available time base. Supported are both external time base (e.g. timepulse pin of GPS chip or oscillator output of RTC chip) and internal ESP32 hardware timer. Selection of time base and clock frequency is done by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h). # Payload format diff --git a/platformio.ini b/platformio.ini index cf7f1ce3..233837e9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,7 +6,7 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -;env_default = generic +env_default = generic ;env_default = ebox ;env_default = eboxtube ;env_default = heltec @@ -15,7 +15,7 @@ ;env_default = ttgov2 ;env_default = ttgov21old ;env_default = ttgov21new -env_default = ttgobeam +;env_default = ttgobeam ;env_default = ttgofox ;env_default = lopy ;env_default = lopy4 @@ -30,10 +30,10 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 1.7.152 +release_version = 1.7.2 ; 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 -debug_level = 4 +debug_level = 0 ; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA upload_protocol = esptool ;upload_protocol = custom diff --git a/src/hal/generic.h b/src/hal/generic.h index 5db463a0..6a787fff 100644 --- a/src/hal/generic.h +++ b/src/hal/generic.h @@ -40,25 +40,28 @@ #define BOARD_HAS_PSRAM // use extra 4MB extern RAM -#define HAS_GPS 1 // use if board has GPS -#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_17, GPIO_NUM_16 // UBlox NEO 6M or 7M with default configuration +// GPS settings +#define HAS_GPS 1 // use on board GPS +#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M +#define GPS_INT GPIO_NUM_34 // 30ns accurary timepulse, to be external wired on pcb: NEO 6M Pin#3 -> GPIO34 +#define GPS_CLK (1000) // pulse length 100ms, accuracy +/- 3 *e-8 [nanoseconds] = 0,95sec / year // Pins for I2C interface of OLED Display #define MY_OLED_SDA (4) #define MY_OLED_SCL (15) #define MY_OLED_RST (16) -// Pins for on board DS3231 RTC chip +// Settings for on board DS3231 RTC chip #define HAS_RTC MY_OLED_SDA, MY_OLED_SCL // SDA, SCL -#define RTC_INT GPIO_NUM_34 // interrupt input from rtc -#define RTC_CLK (1000) // frequency of RTC clock signal in ms +#define RTC_INT GPIO_NUM_34 // timepulse with accuracy +/- 2*e-6 [microseconds] = 0,1728sec / day +#define RTC_CLK (1000) // pulse length 1000ms // Settings for IF482 interface -#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters +//#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters // Settings for DCF77 interface #define HAS_DCF77 GPIO_NUM_1 -//#define DCF77_ACTIVE_LOW 1 +#define DCF77_ACTIVE_LOW 1 // Pins for LORA chip SPI interface, reset line and interrupt lines #define LORA_SCK (5) diff --git a/src/hal/ttgobeam.h b/src/hal/ttgobeam.h index c32994a8..f812e3c9 100644 --- a/src/hal/ttgobeam.h +++ b/src/hal/ttgobeam.h @@ -22,30 +22,27 @@ // GPS settings #define HAS_GPS 1 // use on board GPS -#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M or 7M with default configuration -// to use -#define GPS_INT GPIO_NUM_34 // 30ns accurary timepulse, to be external wired on pcb: R34 -> GPIO34 -// 1 pulse per second, synchronized at rising edge, pulse length 100ms, accuracy +/- 3 *e-8 [nanoseconds] = 0,95sec / year -#define GPS_CLK (1000) +#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M +//#define GPS_INT GPIO_NUM_34 // 30ns accurary timepulse, to be external wired on pcb: NEO 6M Pin#3 -> GPIO34 +//#define GPS_CLK (1000) // pulse length 100ms, accuracy +/- 3 *e-8 [nanoseconds] = 0,95sec / year // enable only if device has these sensors, otherwise comment these lines // BME680 sensor on I2C bus -#define HAS_BME SDA, SCL -#define BME_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !! +//#define HAS_BME SDA, SCL +//#define BME_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !! // display (if connected) -#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C -#define MY_OLED_SDA SDA -#define MY_OLED_SCL SCL -#define MY_OLED_RST U8X8_PIN_NONE +//#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C +//#define MY_OLED_SDA SDA +//#define MY_OLED_SCL SCL +//#define MY_OLED_RST U8X8_PIN_NONE //#define DISPLAY_FLIP 1 // use if display is rotated // Settings for DCF77 interface //#define HAS_DCF77 GPIO_NUM_13 // Settings for IF482 interface -#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters - +//#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters // user defined sensors (if connected) //#define HAS_SENSORS 1 // comment out if device has user defined sensors diff --git a/src/hal/ttgofox.h b/src/hal/ttgofox.h index 7be3544c..3d29d5f3 100644 --- a/src/hal/ttgofox.h +++ b/src/hal/ttgofox.h @@ -23,18 +23,18 @@ // Settings for on board DS3231 RTC chip #define HAS_RTC MY_OLED_SDA, MY_OLED_SCL // SDA, SCL //#define RTC_INT GPIO_NUM_34 // timepulse with accuracy +/- 2*e-6 [microseconds] = 0,1728sec / day -//#define RTC_CLK (1000) // frequency of RTC clock signal in ms +//#define RTC_CLK (1000) // pulse length 1000ms // Settings for IF482 interface //#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters // Settings for DCF77 interface -#define HAS_DCF77 GPIO_NUM_14 -#define DCF77_ACTIVE_LOW 1 +//#define HAS_DCF77 GPIO_NUM_14 +//#define DCF77_ACTIVE_LOW 1 // Settings for external GPS chip -#define HAS_GPS 1 // use on board GPS -#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_17, GPIO_NUM_16 // UBlox NEO 6M or 7M with default configuration +//#define HAS_GPS 1 // use on board GPS +//#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_17, GPIO_NUM_16 // UBlox NEO 6M or 7M with default configuration // Pins for LORA chip SPI interface, reset line and interrupt lines #define LORA_SCK (5)