Merge pull request #266 from cyberman54/development

v1.7.2
This commit is contained in:
Verkehrsrot 2019-02-09 16:15:05 +01:00 committed by GitHub
commit 8baf976419
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 623 additions and 192 deletions

View File

@ -51,7 +51,7 @@ Depending on board hardware following features are supported:
- GPS (Generic serial NMEA, or Quectel L76 I2C) - GPS (Generic serial NMEA, or Quectel L76 I2C)
- Environmental sensor (Bosch BME680 I2C) - Environmental sensor (Bosch BME680 I2C)
- Real Time Clock (Maxim DS3231 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).<br> Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).<br>
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.<br> 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.<br>
@ -138,6 +138,10 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp
- Red long blink: LoRaWAN stack error - Red long blink: LoRaWAN stack error
- White long blink: Known Beacon detected - 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. 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 # Payload format
You can select different payload formats in [paxcounter.conf](src/paxcounter.conf#L12): You can select different payload formats in [paxcounter.conf](src/paxcounter.conf#L12):

View File

@ -11,7 +11,6 @@
#include "bme680mems.h" #include "bme680mems.h"
#endif #endif
// Needed for RTC time sync if RTC present on board
#ifdef HAS_RTC #ifdef HAS_RTC
#include "rtctime.h" #include "rtctime.h"
#endif #endif

18
include/dcf77.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef _DCF77_H
#define _DCF77_H
#include "globals.h"
#include "rtctime.h"
enum dcf_pulses { dcf_off, dcf_zero, dcf_one };
enum dcf_pinstate { dcf_low, dcf_high };
int dcf77_init(void);
void dcf77_loop(void *pvParameters);
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

View File

@ -92,21 +92,25 @@ typedef struct {
float gas; // raw gas sensor signal float gas; // raw gas sensor signal
} bmeStatus_t; } bmeStatus_t;
enum sendprio_t { prio_low, prio_normal, prio_high };
// global variables // global variables
extern configData_t cfg; // current device configuration extern configData_t cfg; // current device configuration
extern char display_line6[], display_line7[]; // screen buffers extern char display_line6[], display_line7[]; // screen buffers
extern uint8_t volatile channel; // wifi channel rotation counter extern uint8_t volatile channel; // wifi channel rotation counter
extern uint16_t volatile macs_total, macs_wifi, macs_ble, extern uint16_t volatile macs_total, macs_wifi, macs_ble,
batt_voltage; // display values batt_voltage; // display values
extern hw_timer_t *channelSwitch, *sendCycle, *displaytimer; extern hw_timer_t *sendCycle, *displaytimer;
extern SemaphoreHandle_t I2Caccess; extern SemaphoreHandle_t I2Caccess;
extern bool volatile BitsPending;
extern std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs; extern std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
extern std::array<uint64_t, 0xff>::iterator it; extern std::array<uint64_t, 0xff>::iterator it;
extern std::array<uint64_t, 0xff> beacons; extern std::array<uint64_t, 0xff> beacons;
extern TaskHandle_t irqHandlerTask, wifiSwitchTask; extern TaskHandle_t irqHandlerTask;
extern Timezone myTZ; // make Timezone myTZ globally available extern TimerHandle_t WifiChanTimer;
extern Timezone myTZ;
// application includes // application includes
#include "led.h" #include "led.h"
@ -149,4 +153,8 @@ extern Timezone myTZ; // make Timezone myTZ globally available
#include "if482.h" #include "if482.h"
#endif #endif
#ifdef HAS_DCF77
#include "dcf77.h"
#endif
#endif #endif

View File

@ -16,5 +16,6 @@ int gps_init(void);
void gps_read(void); void gps_read(void);
void gps_loop(void *pvParameters); void gps_loop(void *pvParameters);
time_t get_gpstime(void); time_t get_gpstime(void);
int gps_config();
#endif #endif

View File

@ -2,12 +2,9 @@
#define _IF482_H #define _IF482_H
#include "globals.h" #include "globals.h"
#include "irqhandler.h" #include "rtctime.h"
extern TaskHandle_t IF482Task;
int if482_init(void); int if482_init(void);
void if482_loop(void *pvParameters); void if482_loop(void *pvParameters);
void IRAM_ATTR IF482IRQ(void);
#endif #endif

View File

@ -11,7 +11,6 @@
#include "senddata.h" #include "senddata.h"
void irqHandler(void *pvParameters); void irqHandler(void *pvParameters);
void IRAM_ATTR ChannelSwitchIRQ();
void IRAM_ATTR homeCycleIRQ(); void IRAM_ATTR homeCycleIRQ();
void IRAM_ATTR SendCycleIRQ(); void IRAM_ATTR SendCycleIRQ();

View File

@ -33,7 +33,7 @@ void os_getDevEui(u1_t *buf);
void showLoraKeys(void); void showLoraKeys(void);
void switch_lora(uint8_t sf, uint8_t tx); void switch_lora(uint8_t sf, uint8_t tx);
void lora_send(osjob_t *job); 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_queuereset(void);
void lora_housekeeping(void); void lora_housekeeping(void);
void user_request_network_time_callback(void *pVoidUserUTCTime, void user_request_network_time_callback(void *pVoidUserUTCTime,

View File

@ -82,7 +82,7 @@ private:
uint8_t cursor; uint8_t cursor;
#else #else
#error "No valid payload converter defined" #error No valid payload converter defined!
#endif #endif
}; };

View File

@ -11,11 +11,18 @@
extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available
extern TaskHandle_t ClockTask;
extern hw_timer_t *clockCycle;
int rtc_init(void); int rtc_init(void);
int set_rtctime(uint32_t t); int set_rtctime(uint32_t t);
int set_rtctime(time_t t); int set_rtctime(time_t t);
void sync_rtctime(void); void sync_rtctime(void);
time_t get_rtctime(void); time_t get_rtctime(void);
float get_rtctemp(void); float get_rtctemp(void);
void IRAM_ATTR CLOCKIRQ();
int timepulse_init(uint32_t pps_freq);
void timepulse_start();
uint8_t sync_clock(time_t t);
#endif // _RTCTIME_H #endif // _RTCTIME_H

View File

@ -5,7 +5,7 @@
#include "lorawan.h" #include "lorawan.h"
#include "cyclic.h" #include "cyclic.h"
void SendPayload(uint8_t port); void SendPayload(uint8_t port, sendprio_t prio);
void sendCounter(void); void sendCounter(void);
void checkSendQueues(void); void checkSendQueues(void);
void flushQueues(); void flushQueues();

View File

@ -28,7 +28,7 @@ licenses. Refer to LICENSE.txt file in repository for more details.
esp_err_t spi_init(); 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_queuereset();
void spi_housekeeping(); void spi_housekeeping();

View File

@ -9,6 +9,6 @@
void wifi_sniffer_init(void); void wifi_sniffer_init(void);
void IRAM_ATTR wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type); 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 #endif

View File

@ -30,10 +30,10 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng
[common] [common]
; for release_version use max. 10 chars total, use any decimal format like "a.b.c" ; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
release_version = 1.7.143 release_version = 1.7.2
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
debug_level = 3 debug_level = 0
; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA ; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA
upload_protocol = esptool upload_protocol = esptool
;upload_protocol = custom ;upload_protocol = custom
@ -45,11 +45,12 @@ monitor_speed = 115200
lib_deps_lora = lib_deps_lora =
MCCI LoRaWAN LMIC library@^2.3.1 MCCI LoRaWAN LMIC library@^2.3.1
lib_deps_display = lib_deps_display =
U8g2@>=2.25.5 U8g2@>=2.25.7
lib_deps_rgbled = lib_deps_rgbled =
SmartLeds@>=1.1.3 SmartLeds@>=1.1.5
lib_deps_gps = lib_deps_gps =
TinyGPSPlus@>=1.0.2 TinyGPSPlus@>=1.0.2
; NeoGPS^4.2.9
lib_deps_rtc = lib_deps_rtc =
RTC@^2.3.0 RTC@^2.3.0
lib_deps_basic = lib_deps_basic =
@ -233,7 +234,7 @@ lib_deps =
${common.lib_deps_basic} ${common.lib_deps_basic}
${common.lib_deps_lora} ${common.lib_deps_lora}
${common.lib_deps_gps} ${common.lib_deps_gps}
; ${common.lib_deps_display} ${common.lib_deps_display}
build_flags = build_flags =
${common.build_flags_all} ${common.build_flags_all}
-mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-issue

View File

@ -10,6 +10,6 @@ void readButton() {
ESP_LOGI(TAG, "Button pressed"); ESP_LOGI(TAG, "Button pressed");
payload.reset(); payload.reset();
payload.addButton(0x01); payload.addButton(0x01);
SendPayload(BUTTONPORT); SendPayload(BUTTONPORT, prio_normal);
} }
#endif #endif

View File

@ -41,7 +41,7 @@ void doHousekeeping() {
if ((millis() >= nextRTCTimeSync) && (timeStatus() == timeSet)) { if ((millis() >= nextRTCTimeSync) && (timeStatus() == timeSet)) {
nextRTCTimeSync = millis() + TIME_WRITE_INTERVAL_RTC * nextRTCTimeSync = millis() + TIME_WRITE_INTERVAL_RTC *
60000; // set up next time sync period 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"); ESP_LOGE(TAG, "RTC set time failure");
else else
ESP_LOGI(TAG, "RTC time updated"); ESP_LOGI(TAG, "RTC time updated");
@ -49,9 +49,6 @@ void doHousekeeping() {
#endif #endif
// task storage debugging // // 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", ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(irqHandlerTask), uxTaskGetStackHighWaterMark(irqHandlerTask),
eTaskGetState(irqHandlerTask)); eTaskGetState(irqHandlerTask));
@ -63,6 +60,10 @@ void doHousekeeping() {
ESP_LOGD(TAG, "Bmeloop %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "Bmeloop %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(BmeTask), eTaskGetState(BmeTask)); uxTaskGetStackHighWaterMark(BmeTask), eTaskGetState(BmeTask));
#endif #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) #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
ESP_LOGD(TAG, "LEDloop %d bytes left | Taskstate = %d", 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 / " "Memory full, counter cleared (heap low water mark = %d Bytes / "
"free heap = %d bytes)", "free heap = %d bytes)",
ESP.getMinFreeHeap(), ESP.getFreeHeap()); 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 reset_counters(); // clear macs container and reset all counters
get_salt(); // get new salt for salting hashes get_salt(); // get new salt for salting hashes
if (ESP.getMinFreeHeap() <= MEM_LOW) // check again if (ESP.getMinFreeHeap() <= MEM_LOW) // check again
do_reset(); // memory leak, reset device do_reset(); // memory leak, reset device
@ -100,9 +101,9 @@ void doHousekeeping() {
#ifdef BOARD_HAS_PSRAM #ifdef BOARD_HAS_PSRAM
if (ESP.getMinFreePsram() <= MEM_LOW) { if (ESP.getMinFreePsram() <= MEM_LOW) {
ESP_LOGI(TAG, "PSRAM full, counter cleared"); 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 reset_counters(); // clear macs container and reset all counters
get_salt(); // get new salt for salting hashes get_salt(); // get new salt for salting hashes
if (ESP.getMinFreePsram() <= MEM_LOW) // check again if (ESP.getMinFreePsram() <= MEM_LOW) // check again
do_reset(); // memory leak, reset device do_reset(); // memory leak, reset device

233
src/dcf77.cpp Normal file
View File

@ -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

View File

@ -42,8 +42,8 @@ const char lora_datarate[] = {"121110090807FSNA"};
#endif #endif
// helper arry for converting month values to text // helper arry for converting month values to text
char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun", const char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
uint8_t volatile DisplayState = 0; uint8_t volatile DisplayState = 0;
@ -145,11 +145,13 @@ void refreshtheDisplay() {
uint8_t msgWaiting; uint8_t msgWaiting;
char buff[16]; // 16 chars line buffer char buff[16]; // 16 chars line buffer
#if (defined HAS_DCF77) || (defined HAS_IF482)
const char timeNosyncSymbol = '?'; const char timeNosyncSymbol = '?';
#ifdef HAS_IF482 #if (defined HAS_IF482)
const char timesyncSymbol = '+'; const char timesyncSymbol = '+';
#else #else
const char timesyncSymbol = '*'; const char timesyncSymbol = '*';
#endif
#endif #endif
// update counter (lines 0-1) // update counter (lines 0-1)
@ -214,23 +216,21 @@ void refreshtheDisplay() {
u8x8.printf("%4dKB", getFreeRAM() / 1024); u8x8.printf("%4dKB", getFreeRAM() / 1024);
#ifdef HAS_LORA #ifdef HAS_LORA
u8x8.setCursor(0, 6); u8x8.setCursor(0, 6);
#ifndef HAS_RTC #if (!defined HAS_DCF77) && (!defined HAS_IF482)
// update LoRa status display (line 6) // update LoRa status display (line 6)
u8x8.printf("%-16s", display_line6); u8x8.printf("%-16s", display_line6);
#else #else // we want a time display instead LoRa status
// update time/date display (line 6) // update time/date display (line 6)
time_t t = myTZ.toLocal(now()); time_t t = myTZ.toLocal(now());
char timeState = char timeState =
timeStatus() == timeSet ? timesyncSymbol : timeNosyncSymbol; timeStatus() == timeSet ? timesyncSymbol : timeNosyncSymbol;
#ifdef RTC_INT // make timestatus symbol blinking // make timestatus symbol blinking if pps line
if (second(t) % 2) if ((BitsPending) && (second(t) % 2))
timeState = ' '; timeState = ' ';
#endif // RTC_INT
u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t), u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t),
timeState, day(t), printmonth[month(t)]); timeState, day(t), printmonth[month(t)]);
#endif // HAS_RTC #endif
// update LMiC event display (line 7) // update LMiC event display (line 7)
u8x8.setCursor(0, 7); u8x8.setCursor(0, 7);

View File

@ -18,6 +18,11 @@ int gps_init(void) {
int ret = 1; int ret = 1;
if (!gps_config()) {
ESP_LOGE(TAG, "GPS chip initializiation error");
return 0;
}
#if defined GPS_SERIAL #if defined GPS_SERIAL
GPS_Serial.begin(GPS_SERIAL); GPS_Serial.begin(GPS_SERIAL);
ESP_LOGI(TAG, "Using serial GPS"); ESP_LOGI(TAG, "Using serial GPS");
@ -41,6 +46,21 @@ int gps_init(void) {
return ret; return ret;
} // gps_init() } // 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 // read GPS data and cast to global struct
void gps_read() { void gps_read() {
gps_status.latitude = (int32_t)(gps.location.lat() * 1e6); 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())) { if ((gps.time.age() < 1500) && (gps.time.isValid())) {
t = tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(), t = tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(),
gps.time.hour(), gps.time.minute(), gps.time.second()); gps.time.hour(), gps.time.minute(), gps.time.second());
ESP_LOGD(TAG, "GPS time: %d/%d/%d %d:%d:%d", gps.date.year(), ESP_LOGD(TAG, "GPS time: %4d/%02d/%02d %02d:%02d:%02d", year(t), month(t),
gps.date.month(), gps.date.day(), gps.time.hour(), day(t), hour(t), minute(t), second(t));
gps.time.minute(), gps.time.second());
} else { } else {
ESP_LOGW(TAG, "GPS has no confident time"); ESP_LOGW(TAG, "GPS has no confident time");
} }

View File

@ -40,20 +40,28 @@
#define BOARD_HAS_PSRAM // use extra 4MB extern RAM #define BOARD_HAS_PSRAM // use extra 4MB extern RAM
#define HAS_GPS 1 // use if board has GPS // GPS settings
#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_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 // Pins for I2C interface of OLED Display
#define MY_OLED_SDA (4) #define MY_OLED_SDA (4)
#define MY_OLED_SCL (15) #define MY_OLED_SCL (15)
#define MY_OLED_RST (16) #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 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 // 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 // Pins for LORA chip SPI interface, reset line and interrupt lines
#define LORA_SCK (5) #define LORA_SCK (5)

View File

@ -19,8 +19,12 @@
#define HAS_BUTTON GPIO_NUM_39 // on board button (next to reset) #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 HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
#define BATT_FACTOR 2 // voltage divider 100k/100k on board #define BATT_FACTOR 2 // voltage divider 100k/100k on board
// GPS settings
#define HAS_GPS 1 // use on board GPS #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 // enable only if device has these sensors, otherwise comment these lines
// BME680 sensor on I2C bus // BME680 sensor on I2C bus
@ -34,6 +38,12 @@
//#define MY_OLED_RST U8X8_PIN_NONE //#define MY_OLED_RST U8X8_PIN_NONE
//#define DISPLAY_FLIP 1 // use if display is rotated //#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) // user defined sensors (if connected)
//#define HAS_SENSORS 1 // comment out if device has user defined sensors //#define HAS_SENSORS 1 // comment out if device has user defined sensors

View File

@ -20,16 +20,21 @@
#define MY_OLED_SCL (22) #define MY_OLED_SCL (22)
#define MY_OLED_RST U8X8_PIN_NONE #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 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 // 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 // Settings for external GPS chip
#define HAS_GPS 1 // use on board GPS //#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 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 // Pins for LORA chip SPI interface, reset line and interrupt lines
#define LORA_SCK (5) #define LORA_SCK (5)

View File

@ -1,5 +1,3 @@
#if defined HAS_IF482 && defined RTC_INT
/* NOTE: /* NOTE:
The IF482 Generator needs an high precise 1 Hz clock signal which cannot be 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 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" #include "if482.h"
// Local logging tag // Local logging tag
static const char TAG[] = "main"; 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) 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) { int if482_init(void) {
// open serial interface // open serial interface
IF482.begin(HAS_IF482); IF482.begin(HAS_IF482);
// use external rtc 1Hz clock for triggering IF482 telegram // start if482 serial output feed task
if (I2C_MUTEX_LOCK()) { xTaskCreatePinnedToCore(if482_loop, // task function
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); "if482loop", // name of task
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); 2048, // stack size of task
I2C_MUTEX_UNLOCK(); (void *)1, // parameter of the task
} else { 3, // priority of the task
ESP_LOGE(TAG, "I2c bus busy - IF482 initialization error"); &ClockTask, // task handle
return 0; 0); // CPU core
}
pinMode(RTC_INT, INPUT_PULLUP);
return 1;
assert(ClockTask); // has clock task started?
timepulse_init(PPS); // setup pulse
timepulse_start(); // start pulse
return 1; // success
} // if482_init } // if482_init
String if482Telegram(time_t tt) { String IF482_Out(time_t tt) {
time_t t = myTZ.toLocal(tt); time_t t = myTZ.toLocal(tt);
char mon, buf[14], out[IF482_FRAME_SIZE];
char mon;
char buf[14] = "000000F000000";
char out[17];
switch (timeStatus()) { // indicates if time has been set and recently synced switch (timeStatus()) { // indicates if time has been set and recently synced
case timeSet: // time is set and is synced case timeSet: // time is set and is synced
@ -128,12 +142,16 @@ String if482Telegram(time_t tt) {
break; break;
} // switch } // switch
if ((timeStatus() == timeSet) || // do we have confident time/date?
(timeStatus() == timeNeedsSync)) // do we have valid time? if ((timeStatus() == timeSet) || (timeStatus() == timeNeedsSync))
snprintf(buf, sizeof buf, "%02u%02u%02u%1u%02u%02u%02u", year(t) - 2000, 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)); 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; return out;
} }
@ -142,16 +160,12 @@ void if482_loop(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
TickType_t wakeTime; TickType_t wakeTime;
time_t t, tt;
const TickType_t timeOffset = const TickType_t timeOffset =
pdMS_TO_TICKS(IF482_OFFSET); // duration of telegram transmit pdMS_TO_TICKS(IF482_OFFSET); // duration of telegram transmit
const TickType_t startTime = xTaskGetTickCount(); // now const TickType_t startTime = xTaskGetTickCount(); // now
// wait until begin of a new second sync_clock(now()); // wait until begin of a new second
t = tt = now(); BitsPending = true; // start blink in display
do {
tt = now();
} while (t == tt);
// take timestamp at moment of start of new second // take timestamp at moment of start of new second
const TickType_t shotTime = xTaskGetTickCount() - startTime - timeOffset; const TickType_t shotTime = xTaskGetTickCount() - startTime - timeOffset;
@ -164,18 +178,22 @@ void if482_loop(void *pvParameters) {
&wakeTime, // receives moment of call from isr &wakeTime, // receives moment of call from isr
portMAX_DELAY); // wait forever (missing error handling here...) portMAX_DELAY); // wait forever (missing error handling here...)
// now we're synced to start of second tt and wait // select clock scale
// until it's time to start transmit telegram for tt+1 #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 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() } // 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 #endif // HAS_IF482

View File

@ -44,11 +44,6 @@ void irqHandler(void *pvParameters) {
// esp32 hardware timer triggered interrupt service routines // esp32 hardware timer triggered interrupt service routines
// they notify the irq handler task // they notify the irq handler task
void IRAM_ATTR ChannelSwitchIRQ() {
xTaskNotifyGive(wifiSwitchTask);
portYIELD_FROM_ISR();
}
void IRAM_ATTR homeCycleIRQ() { void IRAM_ATTR homeCycleIRQ() {
xTaskNotifyFromISR(irqHandlerTask, CYCLIC_IRQ, eSetBits, NULL); xTaskNotifyFromISR(irqHandlerTask, CYCLIC_IRQ, eSetBits, NULL);
portYIELD_FROM_ISR(); portYIELD_FROM_ISR();

View File

@ -34,7 +34,7 @@
// faster or slower. This causes the transceiver to be earlier switched on, // faster or slower. This causes the transceiver to be earlier switched on,
// so consuming more power. You may sharpen (reduce) this value if you are // so consuming more power. You may sharpen (reduce) this value if you are
// limited on battery. // limited on battery.
#define CLOCK_ERROR_PROCENTAGE 30 #define CLOCK_ERROR_PROCENTAGE 3
// Set this to 1 to enable some basic debug output (using printf) about // Set this to 1 to enable some basic debug output (using printf) about
// RF settings used during transmission and reception. Set to 2 to // RF settings used during transmission and reception. Set to 2 to

View File

@ -358,6 +358,7 @@ esp_err_t lora_stack_init() {
#ifndef HAS_LORA #ifndef HAS_LORA
return ESP_OK; // continue main program return ESP_OK; // continue main program
#else #else
assert(SEND_QUEUE_SIZE);
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (LoraSendQueue == 0) { if (LoraSendQueue == 0) {
ESP_LOGE(TAG, "Could not create LORA send queue. Aborting."); ESP_LOGE(TAG, "Could not create LORA send queue. Aborting.");
@ -397,11 +398,20 @@ esp_err_t lora_stack_init() {
#endif #endif
} }
void lora_enqueuedata(MessageBuffer_t *message) { void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) {
// enqueue message in LORA send queue // enqueue message in LORA send queue
#ifdef HAS_LORA #ifdef HAS_LORA
BaseType_t ret = BaseType_t ret;
xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0); 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) { if (ret == pdTRUE) {
ESP_LOGI(TAG, "%d bytes enqueued for LORA interface", message->MessageSize); ESP_LOGI(TAG, "%d bytes enqueued for LORA interface", message->MessageSize);
} else { } else {

View File

@ -107,7 +107,7 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
#endif #endif
payload.reset(); payload.reset();
payload.addAlarm(rssi, beaconID); payload.addAlarm(rssi, beaconID);
SendPayload(BEACONPORT); SendPayload(BEACONPORT, prio_high);
} }
}; };

View File

@ -27,33 +27,33 @@ Uused tasks and timers:
Task Core Prio Purpose Task Core Prio Purpose
==================================================================================== ====================================================================================
wifiloop 0 4 rotates wifi channels
ledloop 0 3 blinks LEDs 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 spiloop 0 2 reads/writes data on spi interface
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
looptask 1 1 arduino core -> runs the LMIC LoRa stack looptask 1 1 arduino core -> runs the LMIC LoRa stack
irqhandler 1 1 executes tasks triggered by irq irqhandler 1 1 executes tasks triggered by hw irq, see table below
gpsloop 1 2 reads data from GPS via serial or i2c gpsloop 1 2 reads data from GPS via serial or i2c
bmeloop 1 1 reads data from BME sensor via i2c bmeloop 1 1 reads data from BME sensor via i2c
IDLE 1 0 ESP32 arduino scheduler IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
Low priority numbers denote low priority tasks. Low priority numbers denote low priority tasks.
Tasks using i2c bus all must have same priority, because using mutex semaphore Tasks using i2c bus all must have same priority, because using mutex semaphore
(irqhandler, bmeloop) (irqhandler, bmeloop)
ESP32 hardware timers ESP32 hardware irq timers
================================ ================================
0 triggers display refresh 0 triggers display refresh
1 triggers Wifi channel switch 1 triggers DCF77 clock signal
2 triggers send payload cycle 2 triggers send payload cycle
3 triggers housekeeping cycle 3 triggers housekeeping cycle
RTC hardware timer (if present) 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 uint8_t volatile channel = 0; // channel rotation counter
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
batt_voltage = 0; // globals for display batt_voltage = 0; // globals for display
hw_timer_t *channelSwitch = NULL, *sendCycle = NULL, *homeCycle = NULL, bool volatile BitsPending = false; // DCF77 or IF482 ticker indicator
*displaytimer = NULL; // irq tasks
TaskHandle_t irqHandlerTask, wifiSwitchTask; hw_timer_t *sendCycle = NULL, *homeCycle = NULL;
#ifdef HAS_DISPLAY
hw_timer_t *displaytimer = NULL;
#endif
TaskHandle_t irqHandlerTask;
SemaphoreHandle_t I2Caccess; SemaphoreHandle_t I2Caccess;
// container holding unique MAC address hashes with Memory Alloctor using PSRAM, // container holding unique MAC address hashes with Memory Alloctor using PSRAM,
@ -93,7 +98,7 @@ void setup() {
char features[100] = ""; char features[100] = "";
I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus
if ((I2Caccess) != NULL) if (I2Caccess)
xSemaphoreGive((I2Caccess)); // Flag the i2c bus available for use xSemaphoreGive((I2Caccess)); // Flag the i2c bus available for use
// disable brownout detection // disable brownout detection
@ -305,11 +310,6 @@ void setup() {
timerAttachInterrupt(homeCycle, &homeCycleIRQ, true); timerAttachInterrupt(homeCycle, &homeCycleIRQ, true);
timerAlarmWrite(homeCycle, HOMECYCLE * 10000, 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 // show payload encoder
#if PAYLOAD_ENCODER == 1 #if PAYLOAD_ENCODER == 1
strcat_P(features, " PLAIN"); strcat_P(features, " PLAIN");
@ -333,6 +333,14 @@ void setup() {
setSyncInterval(TIME_SYNC_INTERVAL_RTC * 60); setSyncInterval(TIME_SYNC_INTERVAL_RTC * 60);
#endif // HAS_RTC #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 // show compiled features
ESP_LOGI(TAG, "Features:%s", features); ESP_LOGI(TAG, "Features:%s", features);
@ -343,7 +351,7 @@ void setup() {
#endif #endif
#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..."); ESP_LOGI(TAG, "Starting Wifi...");
wifi_sniffer_init(); wifi_sniffer_init();
// initialize salt value using esp_random() called by random() in // initialize salt value using esp_random() called by random() in
@ -361,16 +369,6 @@ void setup() {
&irqHandlerTask, // task handle &irqHandlerTask, // task handle
1); // CPU core 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 // initialize bme
#ifdef HAS_BME #ifdef HAS_BME
strcat_P(features, " BME"); strcat_P(features, " BME");
@ -394,7 +392,6 @@ void setup() {
#endif #endif
timerAlarmEnable(sendCycle); timerAlarmEnable(sendCycle);
timerAlarmEnable(homeCycle); timerAlarmEnable(homeCycle);
timerAlarmEnable(channelSwitch);
// start button interrupt // start button interrupt
#ifdef HAS_BUTTON #ifdef HAS_BUTTON
@ -419,21 +416,12 @@ void setup() {
setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60); setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60);
#endif #endif
#if defined HAS_IF482 && defined RTC_INT #ifdef HAS_IF482
strcat_P(features, " IF482");
assert(if482_init());
ESP_LOGI(TAG, "Starting IF482 Generator..."); ESP_LOGI(TAG, "Starting IF482 Generator...");
xTaskCreatePinnedToCore(if482_loop, // task function assert(if482_init());
"if482loop", // name of task #elif defined HAS_DCF77
2048, // stack size of task ESP_LOGI(TAG, "Starting DCF77 Generator...");
(void *)1, // parameter of the task assert(dcf77_init());
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 #endif
} // setup() } // setup()

View File

@ -47,7 +47,7 @@
#define PAYLOAD_BUFFER_SIZE 51 // maximum size of payload block per transmit #define PAYLOAD_BUFFER_SIZE 51 // maximum size of payload block per transmit
#define LORASFDEFAULT 9 // 7 ... 12 SF, according to LoRaWAN specs #define LORASFDEFAULT 9 // 7 ... 12 SF, according to LoRaWAN specs
#define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy #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 // Ports on which the device sends and listenes on LoRaWAN and SPI
#define COUNTERPORT 1 // Port on which device sends counts #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_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_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_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 IF482_OFFSET 984 // 1sec minus IF482 serial transmit time [ms]: e.g. 9 bits * 17 bytes * 1/9600 bps = 16ms #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 // 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 #define DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time

View File

@ -437,5 +437,5 @@ void PayloadConvert::addButton(uint8_t value) {
} }
#else #else
#error "No valid payload converter defined" #error No valid payload converter defined!
#endif #endif

View File

@ -67,8 +67,8 @@ void set_sendcycle(uint8_t val[]) {
void set_wifichancycle(uint8_t val[]) { void set_wifichancycle(uint8_t val[]) {
cfg.wifichancycle = val[0]; cfg.wifichancycle = val[0];
// update channel rotation interrupt // update Wifi channel rotation timer period
timerAlarmWrite(channelSwitch, cfg.wifichancycle * 10000, true); xTimerChangePeriod(WifiChanTimer, pdMS_TO_TICKS(cfg.wifichancycle * 10), 100 );
ESP_LOGI(TAG, ESP_LOGI(TAG,
"Remote command: set Wifi channel switch interval to %.1f seconds", "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"); ESP_LOGI(TAG, "Remote command: get device configuration");
payload.reset(); payload.reset();
payload.addConfig(cfg); payload.addConfig(cfg);
SendPayload(CONFIGPORT); SendPayload(CONFIGPORT, prio_high);
}; };
void get_status(uint8_t val[]) { void get_status(uint8_t val[]) {
@ -247,7 +247,7 @@ void get_status(uint8_t val[]) {
payload.addStatus(voltage, uptime() / 1000, temperatureRead(), payload.addStatus(voltage, uptime() / 1000, temperatureRead(),
getFreeRAM(), rtc_get_reset_reason(0), getFreeRAM(), rtc_get_reset_reason(0),
rtc_get_reset_reason(1)); rtc_get_reset_reason(1));
SendPayload(STATUSPORT); SendPayload(STATUSPORT, prio_high);
}; };
void get_gps(uint8_t val[]) { void get_gps(uint8_t val[]) {
@ -256,7 +256,7 @@ void get_gps(uint8_t val[]) {
gps_read(); gps_read();
payload.reset(); payload.reset();
payload.addGPS(gps_status); payload.addGPS(gps_status);
SendPayload(GPSPORT); SendPayload(GPSPORT, prio_high);
#else #else
ESP_LOGW(TAG, "GPS function not supported"); ESP_LOGW(TAG, "GPS function not supported");
#endif #endif
@ -267,7 +267,7 @@ void get_bme(uint8_t val[]) {
#ifdef HAS_BME #ifdef HAS_BME
payload.reset(); payload.reset();
payload.addBME(bme_status); payload.addBME(bme_status);
SendPayload(BMEPORT); SendPayload(BMEPORT, prio_high);
#else #else
ESP_LOGW(TAG, "BME680 sensor not supported"); ESP_LOGW(TAG, "BME680 sensor not supported");
#endif #endif

View File

@ -1,10 +1,13 @@
#ifdef HAS_RTC
#include "rtctime.h" #include "rtctime.h"
// Local logging tag // Local logging tag
static const char TAG[] = "main"; 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 RtcDS3231<TwoWire> Rtc(Wire); // RTC hardware i2c interface
// initialize RTC // initialize RTC
@ -97,4 +100,95 @@ float get_rtctemp(void) {
return 0; return 0;
} // get_rtctemp() } // 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();
}

View File

@ -2,7 +2,7 @@
#include "senddata.h" #include "senddata.h"
// put data to send in RTos Queues used for transmit over channels Lora and SPI // 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[] MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, Message[]
@ -24,8 +24,8 @@ void SendPayload(uint8_t port) {
memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize()); memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize());
// enqueue message in device's send queues // enqueue message in device's send queues
lora_enqueuedata(&SendBuffer); lora_enqueuedata(&SendBuffer, prio);
spi_enqueuedata(&SendBuffer); spi_enqueuedata(&SendBuffer, prio);
} // SendPayload } // SendPayload
@ -55,7 +55,7 @@ void sendCounter() {
} }
#endif #endif
SendPayload(COUNTERPORT); SendPayload(COUNTERPORT, prio_normal);
// clear counter if not in cumulative counter mode // clear counter if not in cumulative counter mode
if (cfg.countermode != 1) { if (cfg.countermode != 1) {
reset_counters(); // clear macs container and reset all counters reset_counters(); // clear macs container and reset all counters
@ -68,7 +68,7 @@ void sendCounter() {
case MEMS_DATA: case MEMS_DATA:
payload.reset(); payload.reset();
payload.addBME(bme_status); payload.addBME(bme_status);
SendPayload(BMEPORT); SendPayload(BMEPORT, prio_normal);
break; break;
#endif #endif
@ -79,7 +79,7 @@ void sendCounter() {
gps_read(); gps_read();
payload.reset(); payload.reset();
payload.addGPS(gps_status); payload.addGPS(gps_status);
SendPayload(GPSPORT); SendPayload(GPSPORT, prio_high);
} else } else
ESP_LOGD(TAG, "No valid GPS position"); ESP_LOGD(TAG, "No valid GPS position");
break; break;
@ -89,17 +89,17 @@ void sendCounter() {
case SENSOR1_DATA: case SENSOR1_DATA:
payload.reset(); payload.reset();
payload.addSensor(sensor_read(1)); payload.addSensor(sensor_read(1));
SendPayload(SENSOR1PORT); SendPayload(SENSOR1PORT, prio_normal);
break; break;
case SENSOR2_DATA: case SENSOR2_DATA:
payload.reset(); payload.reset();
payload.addSensor(sensor_read(2)); payload.addSensor(sensor_read(2));
SendPayload(SENSOR2PORT); SendPayload(SENSOR2PORT, prio_normal);
break; break;
case SENSOR3_DATA: case SENSOR3_DATA:
payload.reset(); payload.reset();
payload.addSensor(sensor_read(3)); payload.addSensor(sensor_read(3));
SendPayload(SENSOR3PORT); SendPayload(SENSOR3PORT, prio_normal);
break; break;
#endif #endif
@ -107,7 +107,7 @@ void sendCounter() {
case BATT_DATA: case BATT_DATA:
payload.reset(); payload.reset();
payload.addVoltage(read_voltage()); payload.addVoltage(read_voltage());
SendPayload(BATTPORT); SendPayload(BATTPORT, prio_normal);
break; break;
#endif #endif

View File

@ -66,7 +66,8 @@ void spi_slave_task(void *param) {
uint8_t *messageSize = txbuf + 3; uint8_t *messageSize = txbuf + 3;
*messageSize = msg.MessageSize; *messageSize = msg.MessageSize;
memcpy(txbuf + HEADER_SIZE, &msg.Message, 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; uint16_t *crc = (uint16_t *)txbuf;
*crc = crc16_be(0, messageType, msg.MessageSize + HEADER_SIZE - 2); *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 // 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_LOGI(TAG, "Prepared SPI transaction for %zu byte(s)", transaction_size);
ESP_LOG_BUFFER_HEXDUMP(TAG, txbuf, transaction_size, ESP_LOG_DEBUG); 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_LOG_BUFFER_HEXDUMP(TAG, rxbuf, transaction_size, ESP_LOG_DEBUG);
ESP_LOGI(TAG, "Transaction finished with size %zu bits", ESP_LOGI(TAG, "Transaction finished with size %zu bits",
spi_transaction.trans_len); spi_transaction.trans_len);
@ -103,7 +105,7 @@ esp_err_t spi_init() {
#ifndef HAS_SPI #ifndef HAS_SPI
return ESP_OK; return ESP_OK;
#else #else
assert(SEND_QUEUE_SIZE);
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (SPISendQueue == 0) { if (SPISendQueue == 0) {
ESP_LOGE(TAG, "Could not create SPI send queue. Aborting."); ESP_LOGE(TAG, "Could not create SPI send queue. Aborting.");
@ -148,11 +150,20 @@ esp_err_t spi_init() {
#endif #endif
} }
void spi_enqueuedata(MessageBuffer_t *message) { void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio) {
// enqueue message in SPI send queue // enqueue message in SPI send queue
#ifdef HAS_SPI #ifdef HAS_SPI
BaseType_t ret = BaseType_t ret;
xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0); 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) { if (ret == pdTRUE) {
ESP_LOGI(TAG, "%d byte(s) enqueued for SPI interface", ESP_LOGI(TAG, "%d byte(s) enqueued for SPI interface",
message->MessageSize); message->MessageSize);

View File

@ -7,6 +7,8 @@
// Local logging tag // Local logging tag
static const char TAG[] = "wifi"; static const char TAG[] = "wifi";
TimerHandle_t WifiChanTimer;
static wifi_country_t wifi_country = {WIFI_MY_COUNTRY, WIFI_CHANNEL_MIN, static wifi_country_t wifi_country = {WIFI_MY_COUNTRY, WIFI_CHANNEL_MIN,
WIFI_CHANNEL_MAX, 100, WIFI_CHANNEL_MAX, 100,
WIFI_COUNTRY_POLICY_MANUAL}; 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); 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) { void wifi_sniffer_init(void) {
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); wifi_init_config_t wificfg = WIFI_INIT_CONFIG_DEFAULT();
cfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM wificfg.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 wificfg.wifi_task_core_id = 0; // we want wifi task running on core 0
wifi_promiscuous_filter_t filter = { wifi_promiscuous_filter_t filter = {
// .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // only MGMT frames // .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // only MGMT frames
.filter_mask = WIFI_PROMIS_FILTER_MASK_ALL}; // we use all 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_ERROR_CHECK(esp_coex_preference_set(
ESP_COEX_PREFER_BALANCE)); // configure Wifi/BT coexist lib 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_ERROR_CHECK(
esp_wifi_set_country(&wifi_country)); // set locales for RF and channels esp_wifi_set_country(&wifi_country)); // set locales for RF and channels
ESP_ERROR_CHECK( ESP_ERROR_CHECK(
@ -65,16 +74,11 @@ void wifi_sniffer_init(void) {
esp_wifi_set_promiscuous_filter(&filter)); // set MAC frame filter 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_rx_cb(&wifi_sniffer_packet_handler));
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode
}
// Wifi channel rotation task // setup wifi channel rotation timer
void switchWifiChannel(void *parameter) { WifiChanTimer =
while (1) { xTimerCreate("WifiChannelTimer", pdMS_TO_TICKS(cfg.wifichancycle * 10),
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // waiting for channel switch timer pdTRUE, (void *)0, switchWifiChannel);
channel = assert(WifiChanTimer);
(channel % WIFI_CHANNEL_MAX) + 1; // rotate channel 1..WIFI_CHANNEL_MAX xTimerStart(WifiChanTimer, 0);
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); }
//ESP_LOGD(TAG, "Wifi set channel %d", channel);
}
vTaskDelete(NULL); // shoud never be reached
}