35 files changed
+623
-192
No files matched your search
+1
-1
@@ -10,6 +10,6 @@ void readButton() {
|
||||
ESP_LOGI(TAG, "Button pressed");
|
||||
payload.reset();
|
||||
payload.addButton(0x01);
|
||||
SendPayload(BUTTONPORT);
|
||||
SendPayload(BUTTONPORT, prio_normal);
|
||||
}
|
||||
#endif
|
||||
+11
-10
@@ -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));
|
||||
@@ -63,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, "Clockloop %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(ClockTask), eTaskGetState(ClockTask));
|
||||
#endif
|
||||
|
||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||
ESP_LOGD(TAG, "LEDloop %d bytes left | Taskstate = %d",
|
||||
@@ -88,9 +89,9 @@ 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
|
||||
reset_counters(); // clear macs container and reset all counters
|
||||
get_salt(); // get new salt for salting hashes
|
||||
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
|
||||
|
||||
if (ESP.getMinFreeHeap() <= MEM_LOW) // check again
|
||||
do_reset(); // memory leak, reset device
|
||||
@@ -100,9 +101,9 @@ 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
|
||||
reset_counters(); // clear macs container and reset all counters
|
||||
get_salt(); // get new salt for salting hashes
|
||||
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
|
||||
|
||||
if (ESP.getMinFreePsram() <= MEM_LOW) // check again
|
||||
do_reset(); // memory leak, reset device
|
||||
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
// Emulate a DCF77 radio receiver
|
||||
//
|
||||
// a nice & free logic test program for DCF77 can be found here:
|
||||
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!
|
||||
#endif
|
||||
|
||||
#include "dcf77.h"
|
||||
|
||||
// Local logging tag
|
||||
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
|
||||
|
||||
// array of dcf pulses for three minutes
|
||||
uint8_t DCFtimeframe[DCF77_FRAME_SIZE];
|
||||
|
||||
// initialize and configure DCF77 output
|
||||
int dcf77_init(void) {
|
||||
|
||||
BitsPending = false;
|
||||
|
||||
pinMode(HAS_DCF77, OUTPUT);
|
||||
set_DCF77_pin(dcf_low);
|
||||
|
||||
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
|
||||
&ClockTask, // task handle
|
||||
0); // CPU core
|
||||
|
||||
assert(ClockTask); // has clock task started?
|
||||
|
||||
timepulse_init(PPS); // setup pulse
|
||||
DCF_Out(sync_clock(now())); // sync DCF time on next second
|
||||
timepulse_start(); // start pulse
|
||||
|
||||
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) {
|
||||
// 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
|
||||
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...)
|
||||
|
||||
// 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, not yet implemented
|
||||
#error Timepulse is too low for DCF77!
|
||||
#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;
|
||||
time_t t = myTZ.toLocal(tt); // convert to local time
|
||||
|
||||
// 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 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);
|
||||
*/
|
||||
}
|
||||
|
||||
// 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
|
||||
+10
-10
@@ -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;
|
||||
|
||||
@@ -145,11 +145,13 @@ void refreshtheDisplay() {
|
||||
|
||||
uint8_t msgWaiting;
|
||||
char buff[16]; // 16 chars line buffer
|
||||
#if (defined HAS_DCF77) || (defined HAS_IF482)
|
||||
const char timeNosyncSymbol = '?';
|
||||
#ifdef HAS_IF482
|
||||
#if (defined HAS_IF482)
|
||||
const char timesyncSymbol = '+';
|
||||
#else
|
||||
const char timesyncSymbol = '*';
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// update counter (lines 0-1)
|
||||
@@ -214,23 +216,21 @@ void refreshtheDisplay() {
|
||||
u8x8.printf("%4dKB", getFreeRAM() / 1024);
|
||||
|
||||
#ifdef HAS_LORA
|
||||
|
||||
u8x8.setCursor(0, 6);
|
||||
#ifndef HAS_RTC
|
||||
#if (!defined HAS_DCF77) && (!defined HAS_IF482)
|
||||
// update LoRa status display (line 6)
|
||||
u8x8.printf("%-16s", display_line6);
|
||||
#else
|
||||
#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 (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);
|
||||
|
||||
+22
-3
@@ -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,9 +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: %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: %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");
|
||||
}
|
||||
|
||||
+13
-5
@@ -40,20 +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_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
|
||||
|
||||
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
||||
#define LORA_SCK (5)
|
||||
|
||||
+11
-1
@@ -19,8 +19,12 @@
|
||||
#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
|
||||
#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
|
||||
@@ -34,6 +38,12 @@
|
||||
//#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
|
||||
|
||||
// user defined sensors (if connected)
|
||||
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
||||
|
||||
|
||||
+10
-5
@@ -20,16 +20,21 @@
|
||||
#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) // 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_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)
|
||||
|
||||
+58
-40
@@ -1,5 +1,3 @@
|
||||
#if defined HAS_IF482 && defined RTC_INT
|
||||
|
||||
/* 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,42 +77,58 @@ not evaluated by model BU-190
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef HAS_IF482
|
||||
|
||||
#ifdef HAS_DCF77
|
||||
#error You must define at most one of IF482 or DCF77!
|
||||
#endif
|
||||
|
||||
#include "if482.h"
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = "main";
|
||||
|
||||
TaskHandle_t IF482Task;
|
||||
#define IF482_FRAME_SIZE (17)
|
||||
#define IF482_PULSE_DURATION (1000)
|
||||
|
||||
// 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 GPS
|
||||
// initialize and configure IF482 Generator
|
||||
int if482_init(void) {
|
||||
|
||||
// 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;
|
||||
}
|
||||
pinMode(RTC_INT, INPUT_PULLUP);
|
||||
return 1;
|
||||
// start if482 serial output feed task
|
||||
xTaskCreatePinnedToCore(if482_loop, // task function
|
||||
"if482loop", // name of task
|
||||
2048, // stack size of task
|
||||
(void *)1, // parameter of the task
|
||||
3, // priority of the task
|
||||
&ClockTask, // task handle
|
||||
0); // CPU core
|
||||
|
||||
assert(ClockTask); // has clock task started?
|
||||
|
||||
timepulse_init(PPS); // setup pulse
|
||||
timepulse_start(); // start pulse
|
||||
|
||||
return 1; // success
|
||||
} // if482_init
|
||||
|
||||
String if482Telegram(time_t tt) {
|
||||
String IF482_Out(time_t tt) {
|
||||
|
||||
time_t t = myTZ.toLocal(tt);
|
||||
|
||||
char mon;
|
||||
char buf[14] = "000000F000000";
|
||||
char 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
|
||||
@@ -128,12 +142,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;
|
||||
}
|
||||
|
||||
@@ -142,16 +160,12 @@ 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
|
||||
|
||||
// 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
|
||||
const TickType_t shotTime = xTaskGetTickCount() - startTime - timeOffset;
|
||||
@@ -164,18 +178,22 @@ void if482_loop(void *pvParameters) {
|
||||
&wakeTime, // receives moment of call from isr
|
||||
portMAX_DELAY); // wait forever (missing error handling here...)
|
||||
|
||||
// now we're synced to start of second tt and wait
|
||||
// until it's time to start transmit telegram for tt+1
|
||||
// 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(if482Telegram(now() + 1));
|
||||
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, not yet implemented
|
||||
#error Timepulse is too low for IF482!
|
||||
#endif
|
||||
}
|
||||
vTaskDelete(IF482Task); // shoud never be reached
|
||||
} // 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
|
||||
@@ -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();
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+13
-3
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+30
-42
@@ -27,33 +27,33 @@ 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
|
||||
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
|
||||
|
||||
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
|
||||
IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
|
||||
|
||||
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 Wifi channel switch
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
@@ -65,9 +65,14 @@ 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
|
||||
TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
||||
bool volatile BitsPending = false; // DCF77 or IF482 ticker indicator
|
||||
|
||||
hw_timer_t *sendCycle = NULL, *homeCycle = NULL;
|
||||
#ifdef HAS_DISPLAY
|
||||
hw_timer_t *displaytimer = NULL;
|
||||
#endif
|
||||
|
||||
TaskHandle_t irqHandlerTask;
|
||||
SemaphoreHandle_t I2Caccess;
|
||||
|
||||
// container holding unique MAC address hashes with Memory Alloctor using PSRAM,
|
||||
@@ -93,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
|
||||
@@ -305,11 +310,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");
|
||||
@@ -333,6 +333,14 @@ void setup() {
|
||||
setSyncInterval(TIME_SYNC_INTERVAL_RTC * 60);
|
||||
#endif // HAS_RTC
|
||||
|
||||
#if defined HAS_DCF77
|
||||
strcat_P(features, " DCF77");
|
||||
#endif
|
||||
|
||||
#if (defined HAS_IF482) && (defined RTC_INT)
|
||||
strcat_P(features, " IF482");
|
||||
#endif
|
||||
|
||||
// show compiled features
|
||||
ESP_LOGI(TAG, "Features:%s", features);
|
||||
|
||||
@@ -343,7 +351,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 +369,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 +392,6 @@ void setup() {
|
||||
#endif
|
||||
timerAlarmEnable(sendCycle);
|
||||
timerAlarmEnable(homeCycle);
|
||||
timerAlarmEnable(channelSwitch);
|
||||
|
||||
// start button interrupt
|
||||
#ifdef HAS_BUTTON
|
||||
@@ -419,21 +416,12 @@ void setup() {
|
||||
setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60);
|
||||
#endif
|
||||
|
||||
#if defined HAS_IF482 && defined RTC_INT
|
||||
strcat_P(features, " IF482");
|
||||
assert(if482_init());
|
||||
#ifdef HAS_IF482
|
||||
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);
|
||||
assert(if482_init());
|
||||
#elif defined HAS_DCF77
|
||||
ESP_LOGI(TAG, "Starting DCF77 Generator...");
|
||||
assert(dcf77_init());
|
||||
#endif
|
||||
|
||||
} // setup()
|
||||
|
||||
+4
-3
@@ -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
|
||||
@@ -85,8 +85,9 @@
|
||||
#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 IF482_OFFSET 984 // 1sec minus IF482 serial transmit time [ms]: e.g. 9 bits * 17 bytes * 1/9600 bps = 16ms
|
||||
//#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
|
||||
#define DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time
|
||||
|
||||
+1
-1
@@ -437,5 +437,5 @@ void PayloadConvert::addButton(uint8_t value) {
|
||||
}
|
||||
|
||||
#else
|
||||
#error "No valid payload converter defined"
|
||||
#error No valid payload converter defined!
|
||||
#endif
|
||||
+6
-6
@@ -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",
|
||||
@@ -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
|
||||
|
||||
+97
-3
@@ -1,10 +1,13 @@
|
||||
#ifdef HAS_RTC
|
||||
|
||||
#include "rtctime.h"
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = "main";
|
||||
|
||||
TaskHandle_t ClockTask;
|
||||
hw_timer_t *clockCycle = NULL;
|
||||
|
||||
#ifdef HAS_RTC // we have hardware RTC
|
||||
|
||||
RtcDS3231<TwoWire> Rtc(Wire); // RTC hardware i2c interface
|
||||
|
||||
// initialize RTC
|
||||
@@ -97,4 +100,95 @@ float get_rtctemp(void) {
|
||||
return 0;
|
||||
} // get_rtctemp()
|
||||
|
||||
#endif // HAS_RTC
|
||||
#endif // HAS_RTC
|
||||
|
||||
// helper function to setup a pulse for time synchronisation
|
||||
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
|
||||
pinMode(GPS_INT, INPUT_PULLDOWN);
|
||||
// setup external rtc 1Hz clock as pulse per second clock
|
||||
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
|
||||
#elif 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 RTC timepulse");
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
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 {
|
||||
ESP_LOGE(TAG, "I2c bus busy - RTC initialization error");
|
||||
return 0; // failure
|
||||
}
|
||||
return 1; // success
|
||||
|
||||
#else
|
||||
// use ESP32 hardware timer as time base with adjustable frequency
|
||||
if (pulse_period_ms) {
|
||||
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
|
||||
goto pulse_period_error;
|
||||
return 1; // success
|
||||
|
||||
#endif
|
||||
|
||||
pulse_period_error:
|
||||
ESP_LOGE(TAG, "Unknown timepulse period value");
|
||||
return 0; // failure
|
||||
}
|
||||
|
||||
void timepulse_start() {
|
||||
#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
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
+10
-10
@@ -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
|
||||
|
||||
|
||||
+17
-6
@@ -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);
|
||||
|
||||
+20
-16
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user