Merge pull request #177 from cyberman54/development

v1.6.0
This commit is contained in:
Verkehrsrot 2018-10-05 20:10:23 +02:00 committed by GitHub
commit aa00cb896e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 361 additions and 334 deletions

View File

@ -26,7 +26,7 @@ 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.5.16 release_version = 1.6.0
; 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 = 0 debug_level = 0

View File

@ -1,23 +1,12 @@
#ifdef HAS_BUTTON #ifdef HAS_BUTTON
#include "globals.h" #include "globals.h"
#include "senddata.h" #include "button.h"
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
portMUX_TYPE mutexButton = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR ButtonIRQ() {
portENTER_CRITICAL(&mutexButton);
ButtonPressedIRQ++;
portEXIT_CRITICAL(&mutexButton);
}
void readButton() { void readButton() {
portENTER_CRITICAL(&mutexButton);
ButtonPressedIRQ = 0;
portEXIT_CRITICAL(&mutexButton);
ESP_LOGI(TAG, "Button pressed"); ESP_LOGI(TAG, "Button pressed");
payload.reset(); payload.reset();
payload.addButton(0x01); payload.addButton(0x01);

View File

@ -1,7 +1,8 @@
#ifndef _BUTTON_H #ifndef _BUTTON_H
#define _BUTTON_H #define _BUTTON_H
void IRAM_ATTR ButtonIRQ(void); #include "senddata.h"
void readButton(void);
void readButton();
#endif #endif

View File

@ -2,22 +2,14 @@
/* Interval can be set in paxcounter.conf (HOMECYCLE) */ /* Interval can be set in paxcounter.conf (HOMECYCLE) */
// Basic config // Basic config
#include "globals.h" #include "cyclic.h"
#include "senddata.h"
#include "ota.h"
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
portMUX_TYPE mutexHomeCycle = portMUX_INITIALIZER_UNLOCKED;
// do all housekeeping // do all housekeeping
void doHousekeeping() { void doHousekeeping() {
portENTER_CRITICAL(&mutexHomeCycle);
HomeCycleIRQ = 0;
portEXIT_CRITICAL(&mutexHomeCycle);
// update uptime counter // update uptime counter
uptime(); uptime();
@ -26,17 +18,19 @@ void doHousekeeping() {
ESP.restart(); ESP.restart();
// task storage debugging // // task storage debugging //
#ifdef HAS_LORA
ESP_LOGD(TAG, "Loraloop %d bytes left",
uxTaskGetStackHighWaterMark(LoraTask));
#endif
ESP_LOGD(TAG, "Wifiloop %d bytes left", ESP_LOGD(TAG, "Wifiloop %d bytes left",
uxTaskGetStackHighWaterMark(wifiSwitchTask)); uxTaskGetStackHighWaterMark(wifiSwitchTask));
ESP_LOGD(TAG, "Statemachine %d bytes left", ESP_LOGD(TAG, "IRQhandler %d bytes left",
uxTaskGetStackHighWaterMark(stateMachineTask)); uxTaskGetStackHighWaterMark(irqHandlerTask));
#ifdef HAS_GPS #ifdef HAS_GPS
ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask)); ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask));
#endif #endif
#ifdef HAS_SPI
ESP_LOGD(TAG, "Spiloop %d bytes left", uxTaskGetStackHighWaterMark(SpiTask));
#endif
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
ESP_LOGD(TAG, "LEDloop %d bytes left", uxTaskGetStackHighWaterMark(ledLoopTask));
#endif
// read battery voltage into global variable // read battery voltage into global variable
#ifdef HAS_BATTERY_PROBE #ifdef HAS_BATTERY_PROBE
@ -70,12 +64,6 @@ void doHousekeeping() {
} }
} // doHousekeeping() } // doHousekeeping()
void IRAM_ATTR homeCycleIRQ() {
portENTER_CRITICAL(&mutexHomeCycle);
HomeCycleIRQ++;
portEXIT_CRITICAL(&mutexHomeCycle);
}
// uptime counter 64bit to prevent millis() rollover after 49 days // uptime counter 64bit to prevent millis() rollover after 49 days
uint64_t uptime() { uint64_t uptime() {
static uint32_t low32, high32; static uint32_t low32, high32;

View File

@ -1,8 +1,10 @@
#ifndef _CYCLIC_H #ifndef _CYCLIC_H
#define _CYCLIC_H #define _CYCLIC_H
#include "globals.h"
#include "senddata.h"
void doHousekeeping(void); void doHousekeeping(void);
void IRAM_ATTR homeCycleIRQ(void);
uint64_t uptime(void); uint64_t uptime(void);
void reset_counters(void); void reset_counters(void);
int redirect_log(const char *fmt, va_list args); int redirect_log(const char *fmt, va_list args);

View File

@ -15,8 +15,6 @@ const char lora_datarate[] = {"100908078CNA121110090807"};
uint8_t volatile DisplayState = 0; uint8_t volatile DisplayState = 0;
portMUX_TYPE mutexDisplay = portMUX_INITIALIZER_UNLOCKED;
// helper function, prints a hex key on display // helper function, prints a hex key on display
void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) { void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) {
const uint8_t *p; const uint8_t *p;
@ -27,8 +25,9 @@ void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) {
u8x8.printf("\n"); u8x8.printf("\n");
} }
// show startup screen
void init_display(const char *Productname, const char *Version) { void init_display(const char *Productname, const char *Version) {
// show startup screen
uint8_t buf[32]; uint8_t buf[32];
u8x8.begin(); u8x8.begin();
u8x8.setFont(u8x8_font_chroma48medium8_r); u8x8.setFont(u8x8_font_chroma48medium8_r);
@ -93,10 +92,6 @@ void init_display(const char *Productname, const char *Version) {
void refreshtheDisplay() { void refreshtheDisplay() {
portENTER_CRITICAL(&mutexDisplay);
DisplayTimerIRQ = 0;
portEXIT_CRITICAL(&mutexDisplay);
// set display on/off according to current device configuration // set display on/off according to current device configuration
if (DisplayState != cfg.screenon) { if (DisplayState != cfg.screenon) {
DisplayState = cfg.screenon; DisplayState = cfg.screenon;
@ -107,7 +102,7 @@ void refreshtheDisplay() {
if (!DisplayState) if (!DisplayState)
return; return;
uint8_t msgWaiting = 0; uint8_t msgWaiting;
char buff[16]; // 16 chars line buffer char buff[16]; // 16 chars line buffer
// update counter (lines 0-1) // update counter (lines 0-1)
@ -192,10 +187,4 @@ void refreshtheDisplay() {
} // refreshDisplay() } // refreshDisplay()
void IRAM_ATTR DisplayIRQ() {
portENTER_CRITICAL_ISR(&mutexDisplay);
DisplayTimerIRQ++;
portEXIT_CRITICAL_ISR(&mutexDisplay);
}
#endif // HAS_DISPLAY #endif // HAS_DISPLAY

View File

@ -9,6 +9,5 @@ extern HAS_DISPLAY u8x8;
void init_display(const char *Productname, const char *Version); void init_display(const char *Productname, const char *Version);
void refreshtheDisplay(void); void refreshtheDisplay(void);
void DisplayKey(const uint8_t *key, uint8_t len, bool lsb); void DisplayKey(const uint8_t *key, uint8_t len, bool lsb);
void IRAM_ATTR DisplayIRQ(void);
#endif #endif

View File

@ -46,34 +46,28 @@ extern uint16_t volatile macs_total, macs_wifi, macs_ble,
batt_voltage; // display values batt_voltage; // display values
extern std::set<uint16_t> macs; // temp storage for MACs extern std::set<uint16_t> macs; // temp storage for MACs
extern hw_timer_t *channelSwitch, *sendCycle; extern hw_timer_t *channelSwitch, *sendCycle;
extern volatile uint8_t SendCycleTimerIRQ, HomeCycleIRQ, DisplayTimerIRQ,
ChannelTimerIRQ, ButtonPressedIRQ;
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 SemaphoreHandle_t xWifiChannelSwitchSemaphore; extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
extern TaskHandle_t stateMachineTask, wifiSwitchTask;
#ifdef HAS_GPS #ifdef HAS_GPS
extern TaskHandle_t GpsTask;
#include "gps.h" #include "gps.h"
#endif #endif
#ifdef HAS_LED #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
#include "led.h" #include "led.h"
#endif #endif
#include "payload.h" #include "payload.h"
#ifdef HAS_LORA #ifdef HAS_LORA
extern QueueHandle_t LoraSendQueue;
extern TaskHandle_t LoraTask;
#include "lorawan.h" #include "lorawan.h"
#endif #endif
#ifdef HAS_SPI #ifdef HAS_SPI
extern QueueHandle_t SPISendQueue; #include "spi.h"
#endif #endif
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY

View File

@ -7,6 +7,7 @@ static const char TAG[] = "main";
TinyGPSPlus gps; TinyGPSPlus gps;
gpsStatus_t gps_status; gpsStatus_t gps_status;
TaskHandle_t GpsTask;
// read GPS data and cast to global struct // read GPS data and cast to global struct
void gps_read() { void gps_read() {
@ -67,6 +68,8 @@ void gps_loop(void *pvParameters) {
} // end of infinite loop } // end of infinite loop
vTaskDelete(NULL); // shoud never be reached
} // gps_loop() } // gps_loop()
#endif // HAS_GPS #endif // HAS_GPS

View File

@ -19,6 +19,7 @@ typedef struct {
extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe
extern gpsStatus_t extern gpsStatus_t
gps_status; // Make struct for storing gps data globally available gps_status; // Make struct for storing gps data globally available
extern TaskHandle_t GpsTask;
void gps_read(void); void gps_read(void);
void gps_loop(void *pvParameters); void gps_loop(void *pvParameters);

74
src/irqhandler.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "irqhandler.h"
// Local logging tag
static const char TAG[] = "main";
// irq handler task, handles all our application level interrupts
void irqHandler(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
uint32_t InterruptStatus;
// task remains in blocked state until it is notified by an irq
for (;;) {
xTaskNotifyWait(
0x00, // Don't clear any bits on entry
ULONG_MAX, // Clear all bits on exit
&InterruptStatus, // Receives the notification value
portMAX_DELAY); // wait forever (missing error handling here...)
// button pressed?
#ifdef HAS_BUTTON
if (InterruptStatus & BUTTON_IRQ)
readButton();
#endif
// display needs refresh?
#ifdef HAS_DISPLAY
if (InterruptStatus & DISPLAY_IRQ)
refreshtheDisplay();
#endif
// are cyclic tasks due?
if (InterruptStatus & CYCLIC_IRQ)
doHousekeeping();
// is time to send the payload?
if (InterruptStatus & SENDPAYLOAD_IRQ)
sendPayload();
}
vTaskDelete(NULL); // shoud never be reached
}
// 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();
}
void IRAM_ATTR SendCycleIRQ() {
xTaskNotifyFromISR(irqHandlerTask, SENDPAYLOAD_IRQ, eSetBits, NULL);
portYIELD_FROM_ISR();
}
#ifdef HAS_DISPLAY
void IRAM_ATTR DisplayIRQ() {
xTaskNotifyFromISR(irqHandlerTask, DISPLAY_IRQ, eSetBits, NULL);
portYIELD_FROM_ISR();
}
#endif
#ifdef HAS_BUTTON
void IRAM_ATTR ButtonIRQ() {
xTaskNotifyFromISR(irqHandlerTask, BUTTON_IRQ, eSetBits, NULL);
portYIELD_FROM_ISR();
}
#endif

28
src/irqhandler.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _IRQHANDLER_H
#define _IRQHANDLER_H
#define DISPLAY_IRQ 0x01
#define BUTTON_IRQ 0x02
#define SENDPAYLOAD_IRQ 0x04
#define CYCLIC_IRQ 0x08
#include "globals.h"
#include "cyclic.h"
#include "senddata.h"
void irqHandler(void *pvParameters);
void IRAM_ATTR ChannelSwitchIRQ();
void IRAM_ATTR homeCycleIRQ();
void IRAM_ATTR SendCycleIRQ();
#ifdef HAS_DISPLAY
#include "display.h"
void IRAM_ATTR DisplayIRQ();
#endif
#ifdef HAS_BUTTON
#include "button.h"
void IRAM_ATTR ButtonIRQ();
#endif
#endif

View File

@ -6,6 +6,8 @@ led_states LEDState = LED_OFF; // LED state global for state machine
led_states previousLEDState = led_states previousLEDState =
LED_ON; // This will force LED to be off at boot since State is OFF LED_ON; // This will force LED to be off at boot since State is OFF
TaskHandle_t ledLoopTask;
uint16_t LEDColor = COLOR_NONE, LEDBlinkDuration = 0; // state machine variables uint16_t LEDColor = COLOR_NONE, LEDBlinkDuration = 0; // state machine variables
unsigned long LEDBlinkStarted = 0; // When (in millis() led blink started) unsigned long LEDBlinkStarted = 0; // When (in millis() led blink started)
@ -94,7 +96,8 @@ void blink_LED(uint16_t set_color, uint16_t set_blinkduration) {
LEDState = LED_ON; // Let main set LED on LEDState = LED_ON; // Let main set LED on
} }
void led_loop() { void ledLoop(void *parameter) {
while (1) {
// Custom blink running always have priority other LoRaWAN led management // Custom blink running always have priority other LoRaWAN led management
if (LEDBlinkStarted && LEDBlinkDuration) { if (LEDBlinkStarted && LEDBlinkDuration) {
// Custom blink is finished, let this order, avoid millis() overflow // Custom blink is finished, let this order, avoid millis() overflow
@ -116,7 +119,8 @@ void led_loop() {
if (LMIC.opmode & (OP_JOINING | OP_REJOIN)) { if (LMIC.opmode & (OP_JOINING | OP_REJOIN)) {
LEDColor = COLOR_YELLOW; LEDColor = COLOR_YELLOW;
// quick blink 20ms on each 1/5 second // quick blink 20ms on each 1/5 second
LEDState = ((millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending LEDState =
((millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending
} else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) { } else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) {
LEDColor = COLOR_BLUE; LEDColor = COLOR_BLUE;
// small blink 10ms on each 1/2sec (not when joining) // small blink 10ms on each 1/2sec (not when joining)
@ -157,6 +161,10 @@ void led_loop() {
} }
previousLEDState = LEDState; previousLEDState = LEDState;
} }
}; // led_loop() // give yield to CPU
vTaskDelay(2 / portTICK_PERIOD_MS);
} // while(1)
vTaskDelete(NULL); // shoud never be reached
}; // ledloop()
#endif // #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) #endif // #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)

View File

@ -31,9 +31,11 @@ struct RGBColor {
enum led_states { LED_OFF, LED_ON }; enum led_states { LED_OFF, LED_ON };
extern TaskHandle_t ledLoopTask;
// Exported Functions // Exported Functions
void rgb_set_color(uint16_t hue); void rgb_set_color(uint16_t hue);
void blink_LED(uint16_t set_color, uint16_t set_blinkduration); void blink_LED(uint16_t set_color, uint16_t set_blinkduration);
void led_loop(); void ledLoop(void *parameter);
#endif #endif

View File

@ -6,6 +6,9 @@
// Local logging Tag // Local logging Tag
static const char TAG[] = "lora"; static const char TAG[] = "lora";
osjob_t sendjob;
QueueHandle_t LoraSendQueue;
// LMIC enhanced Pin mapping // LMIC enhanced Pin mapping
const lmic_pinmap lmic_pins = {.mosi = PIN_SPI_MOSI, const lmic_pinmap lmic_pins = {.mosi = PIN_SPI_MOSI,
.miso = PIN_SPI_MISO, .miso = PIN_SPI_MISO,
@ -204,6 +207,9 @@ void onEvent(ev_t ev) {
// the library) // the library)
switch_lora(cfg.lorasf, cfg.txpower); switch_lora(cfg.lorasf, cfg.txpower);
// kickoff first send job
os_setCallback(&sendjob, lora_send);
// show effective LoRa parameters after join // show effective LoRa parameters after join
ESP_LOGI(TAG, "ADR=%d, SF=%d, TXPOWER=%d", cfg.adrmode, cfg.lorasf, ESP_LOGI(TAG, "ADR=%d, SF=%d, TXPOWER=%d", cfg.adrmode, cfg.lorasf,
cfg.txpower); cfg.txpower);
@ -241,17 +247,6 @@ void onEvent(ev_t ev) {
} // onEvent() } // onEvent()
// LMIC FreeRTos Task
void lorawan_loop(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
while (1) {
os_runloop_once(); // execute LMIC jobs
vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU
}
}
// helper function to assign LoRa datarates to numeric spreadfactor values // helper function to assign LoRa datarates to numeric spreadfactor values
void switch_lora(uint8_t sf, uint8_t tx) { void switch_lora(uint8_t sf, uint8_t tx) {
if (tx > 20) if (tx > 20)
@ -299,4 +294,25 @@ void switch_lora(uint8_t sf, uint8_t tx) {
} }
} }
void lora_send(osjob_t *job) {
MessageBuffer_t SendBuffer;
// Check if there is a pending TX/RX job running, if yes don't eat data
// since it cannot be sent right now
if ((LMIC.opmode & (OP_JOINING | OP_REJOIN | OP_TXDATA | OP_POLL)) != 0) {
// waiting for LoRa getting ready
} else {
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
// SendBuffer gets struct MessageBuffer with next payload from queue
LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
SendBuffer.MessageSize, (cfg.countermode & 0x02));
ESP_LOGI(TAG, "%d bytes sent to LoRa", SendBuffer.MessageSize);
sprintf(display_line7, "PACKET QUEUED");
}
}
// reschedule job every 0,5 - 1 sec. including a bit of random to prevent
// systematic collisions
os_setTimedCallback(job, os_getTime() + 500 + ms2osticks(random(500)),
lora_send);
}
#endif // HAS_LORA #endif // HAS_LORA

View File

@ -14,6 +14,8 @@
#include <Wire.h> #include <Wire.h>
#endif #endif
extern QueueHandle_t LoraSendQueue;
void onEvent(ev_t ev); void onEvent(ev_t ev);
void gen_lora_deveui(uint8_t *pdeveui); void gen_lora_deveui(uint8_t *pdeveui);
void RevBytes(unsigned char *b, size_t c); void RevBytes(unsigned char *b, size_t c);
@ -22,7 +24,7 @@ void os_getDevKey(u1_t *buf);
void os_getArtEui(u1_t *buf); void os_getArtEui(u1_t *buf);
void os_getDevEui(u1_t *buf); void os_getDevEui(u1_t *buf);
void showLoraKeys(void); void showLoraKeys(void);
void lorawan_loop(void *pvParameters);
void switch_lora(uint8_t sf, uint8_t tx); void switch_lora(uint8_t sf, uint8_t tx);
void lora_send(osjob_t *job);
#endif #endif

View File

@ -27,19 +27,22 @@ Uused tasks and timers:
Task Core Prio Purpose Task Core Prio Purpose
==================================================================================== ====================================================================================
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer task
gpsloop 0 2 read data from GPS over serial or i2c
IDLE 1 0 Arduino loop() -> used for LED switching
loraloop 1 2 runs the LMIC stack
statemachine 1 1 switches application process logic
wifiloop 0 4 rotates wifi channels wifiloop 0 4 rotates wifi channels
ledloop 0 3 blinks LEDs
gpsloop 0 2 reads data from GPS over serial or i2c
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
IDLE 1 0 ESP32 arduino scheduler
ESP32 hardware timers ESP32 hardware timers
========================== ==========================
0 Display-Refresh 0 Trigger display refresh
1 Wifi Channel Switch 1 Trigger Wifi channel switch
2 Send Cycle 2 Trigger send payload cycle
3 Housekeeping 3 Trigger housekeeping cycle
*/ */
@ -52,30 +55,8 @@ 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
// hardware timer for cyclic tasks hw_timer_t *channelSwitch, *sendCycle, *homeCycle, *displaytimer; // irq tasks
hw_timer_t *channelSwitch, *displaytimer, *sendCycle, *homeCycle; TaskHandle_t irqHandlerTask, wifiSwitchTask;
// this variables will be changed in the ISR, and read in main loop
uint8_t volatile ButtonPressedIRQ = 0, ChannelTimerIRQ = 0,
SendCycleTimerIRQ = 0, DisplayTimerIRQ = 0, HomeCycleIRQ = 0;
TaskHandle_t stateMachineTask, wifiSwitchTask;
SemaphoreHandle_t xWifiChannelSwitchSemaphore;
// RTos send queues for payload transmit
#ifdef HAS_LORA
QueueHandle_t LoraSendQueue;
TaskHandle_t LoraTask = NULL;
#endif
#ifdef HAS_SPI
QueueHandle_t SPISendQueue;
#endif
#ifdef HAS_GPS
TaskHandle_t GpsTask = NULL;
#endif
std::set<uint16_t> macs; // container holding unique MAC adress hashes std::set<uint16_t> macs; // container holding unique MAC adress hashes
@ -159,12 +140,10 @@ void setup() {
strcat_P(features, "PU"); strcat_P(features, "PU");
// install button interrupt (pullup mode) // install button interrupt (pullup mode)
pinMode(HAS_BUTTON, INPUT_PULLUP); pinMode(HAS_BUTTON, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, RISING);
#else #else
strcat_P(features, "PD"); strcat_P(features, "PD");
// install button interrupt (pulldown mode) // install button interrupt (pulldown mode)
pinMode(HAS_BUTTON, INPUT_PULLDOWN); pinMode(HAS_BUTTON, INPUT_PULLDOWN);
attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, FALLING);
#endif // BUTTON_PULLUP #endif // BUTTON_PULLUP
#endif // HAS_BUTTON #endif // HAS_BUTTON
@ -183,6 +162,16 @@ void setup() {
} else } else
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes", ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE); SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
ESP_LOGI(TAG, "Starting LMIC...");
os_init(); // initialize lmic run-time environment on core 1
LMIC_reset(); // initialize lmic MAC
LMIC_setClockError(MAX_CLOCK_ERROR * 1 /
100); // This tells LMIC to make the receive windows
// bigger, in case your clock is 1% faster or slower.
LMIC_startJoining(); // start joining
#endif #endif
// initialize SPI // initialize SPI
@ -232,16 +221,12 @@ void setup() {
// setup display refresh trigger IRQ using esp32 hardware timer // setup display refresh trigger IRQ using esp32 hardware timer
// https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ // https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/
// prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up // prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up
displaytimer = timerBegin(0, 80, true); displaytimer = timerBegin(0, 80, true);
// interrupt handler DisplayIRQ, triggered by edge // interrupt handler DisplayIRQ, triggered by edge
timerAttachInterrupt(displaytimer, &DisplayIRQ, true); timerAttachInterrupt(displaytimer, &DisplayIRQ, true);
// reload interrupt after each trigger of display refresh cycle // reload interrupt after each trigger of display refresh cycle
timerAlarmWrite(displaytimer, DISPLAYREFRESH_MS * 1000, true); timerAlarmWrite(displaytimer, DISPLAYREFRESH_MS * 1000, true);
// enable display interrupt
yield();
timerAlarmEnable(displaytimer);
#endif #endif
// setup send cycle trigger IRQ using esp32 hardware timer 2 // setup send cycle trigger IRQ using esp32 hardware timer 2
@ -255,20 +240,10 @@ void setup() {
timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true); timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true);
// setup channel rotation trigger IRQ using esp32 hardware timer 1 // setup channel rotation trigger IRQ using esp32 hardware timer 1
xWifiChannelSwitchSemaphore = xSemaphoreCreateBinary();
channelSwitch = timerBegin(1, 800, true); channelSwitch = timerBegin(1, 800, true);
timerAttachInterrupt(channelSwitch, &ChannelSwitchIRQ, true); timerAttachInterrupt(channelSwitch, &ChannelSwitchIRQ, true);
timerAlarmWrite(channelSwitch, cfg.wifichancycle * 1000, true); timerAlarmWrite(channelSwitch, cfg.wifichancycle * 1000, true);
// enable timers
// caution, see: https://github.com/espressif/arduino-esp32/issues/1313
yield();
timerAlarmEnable(homeCycle);
yield();
timerAlarmEnable(sendCycle);
yield();
timerAlarmEnable(channelSwitch);
// show payload encoder // show payload encoder
#if PAYLOAD_ENCODER == 1 #if PAYLOAD_ENCODER == 1
strcat_P(features, " PLAIN"); strcat_P(features, " PLAIN");
@ -288,43 +263,6 @@ void setup() {
#ifdef VERBOSE #ifdef VERBOSE
showLoraKeys(); showLoraKeys();
#endif #endif
// initialize LoRaWAN LMIC run-time environment
os_init();
// reset LMIC MAC state
LMIC_reset();
// This tells LMIC to make the receive windows bigger, in case your clock is
// 1% faster or slower.
LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100);
// join network
LMIC_startJoining();
// start lmic runloop in rtos task on core 1
// (note: arduino main loop runs on core 1, too)
// https://techtutorialsx.com/2017/05/09/esp32-get-task-execution-core/
ESP_LOGI(TAG, "Starting Lora...");
xTaskCreatePinnedToCore(lorawan_loop, /* task function */
"loraloop", /* name of task */
3048, /* stack size of task */
(void *)1, /* parameter of the task */
2, /* priority of the task */
&LoraTask, /* task handle*/
1); /* CPU core */
#endif
// if device has GPS and it is enabled, start GPS reader task on core 0 with
// higher priority than wifi channel rotation task since we process serial
// streaming NMEA data
#ifdef HAS_GPS
ESP_LOGI(TAG, "Starting GPS...");
xTaskCreatePinnedToCore(gps_loop, /* task function */
"gpsloop", /* name of task */
1024, /* stack size of task */
(void *)1, /* parameter of the task */
2, /* priority of the task */
&GpsTask, /* task handle*/
0); /* CPU core */
#endif #endif
// start BLE scan callback if BLE function is enabled in NVRAM configuration // start BLE scan callback if BLE function is enabled in NVRAM configuration
@ -343,34 +281,88 @@ void setup() {
// function gets it's seed from RF noise // function gets it's seed from RF noise
get_salt(); // get new 16bit for salting hashes get_salt(); // get new 16bit for salting hashes
// start wifi channel rotation task #ifdef HAS_GPS
xTaskCreatePinnedToCore(switchWifiChannel, /* task function */ ESP_LOGI(TAG, "Starting GPSloop...");
"wifiloop", /* name of task */ xTaskCreatePinnedToCore(gps_loop, // task function
2048, /* stack size of task */ "gpsloop", // name of task
NULL, /* parameter of the task */ 1024, // stack size of task
4, /* priority of the task */ (void *)1, // parameter of the task
&wifiSwitchTask, /* task handle*/ 2, // priority of the task
0); /* CPU core */ &GpsTask, // task handle
0); // CPU core
#endif
#ifdef HAS_SPI
ESP_LOGI(TAG, "Starting SPIloop...");
xTaskCreatePinnedToCore(spi_loop, // task function
"spiloop", // name of task
2048, // stack size of task
(void *)1, // parameter of the task
2, // priority of the task
&SpiTask, // task handle
0); // CPU core
#endif
// start state machine // start state machine
ESP_LOGI(TAG, "Starting Statemachine..."); ESP_LOGI(TAG, "Starting IRQ Handler...");
xTaskCreatePinnedToCore(stateMachine, /* task function */ xTaskCreatePinnedToCore(irqHandler, // task function
"stateloop", /* name of task */ "irqhandler", // name of task
2048, /* stack size of task */ 2048, // stack size of task
(void *)1, /* parameter of the task */ (void *)1, // parameter of the task
1, /* priority of the task */ 1, // priority of the task
&stateMachineTask, /* task handle */ &irqHandlerTask, // task handle
1); /* CPU core */ 1); // CPU core
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
// start led loop
ESP_LOGI(TAG, "Starting LEDloop...");
xTaskCreatePinnedToCore(ledLoop, // task function
"ledloop", // name of task
1024, // stack size of task
(void *)1, // parameter of the task
3, // priority of the task
&ledLoopTask, // task handle
0); // CPU core
#endif
// 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
// start timer triggered interrupts
ESP_LOGI(TAG, "Starting Interrupts...");
#ifdef HAS_DISPLAY
timerAlarmEnable(displaytimer);
#endif
timerAlarmEnable(sendCycle);
timerAlarmEnable(homeCycle);
timerAlarmEnable(channelSwitch);
// start button interrupt
#ifdef HAS_BUTTON
#ifdef BUTTON_PULLUP
attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, RISING);
#else
attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, FALLING);
#endif
#endif // HAS_BUTTON
} // setup() } // setup()
void loop() { void loop() {
// switch LED state if device has LED(s) while (1) {
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) #ifdef HAS_LORA
led_loop(); os_runloop_once(); // execute lmic scheduled jobs and events
#endif #endif
vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU
// give yield to CPU }
vTaskDelay(2 / portTICK_PERIOD_MS);
vTaskDelete(NULL); // shoud never be reached
} }

View File

@ -6,12 +6,11 @@
#include <esp32-hal-timer.h> // needed for timers #include <esp32-hal-timer.h> // needed for timers
#include "globals.h" #include "globals.h"
#include "led.h"
#include "wifiscan.h" #include "wifiscan.h"
#include "configmanager.h" #include "configmanager.h"
#include "cyclic.h" #include "cyclic.h"
#include "beacon_array.h" #include "beacon_array.h"
#include "ota.h" #include "ota.h"
#include "statemachine.h" #include "irqhandler.h"
#endif #endif

View File

@ -1,8 +1,6 @@
// Basic Config // Basic Config
#include "globals.h" #include "globals.h"
portMUX_TYPE mutexSendCycle = portMUX_INITIALIZER_UNLOCKED;
// 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 SendData(uint8_t port) { void SendData(uint8_t port) {
@ -39,10 +37,6 @@ void SendData(uint8_t port) {
// interrupt triggered function to prepare payload to send // interrupt triggered function to prepare payload to send
void sendPayload() { void sendPayload() {
portENTER_CRITICAL(&mutexSendCycle);
SendCycleTimerIRQ = 0;
portEXIT_CRITICAL(&mutexSendCycle);
// append counter data to payload // append counter data to payload
payload.reset(); payload.reset();
payload.addCount(macs_wifi, cfg.blescan ? macs_ble : 0); payload.addCount(macs_wifi, cfg.blescan ? macs_ble : 0);
@ -68,40 +62,6 @@ void sendPayload() {
SendData(COUNTERPORT); SendData(COUNTERPORT);
} // sendpayload() } // sendpayload()
// interrupt handler used for payload send cycle timer
void IRAM_ATTR SendCycleIRQ() {
portENTER_CRITICAL(&mutexSendCycle);
SendCycleTimerIRQ++;
portEXIT_CRITICAL(&mutexSendCycle);
}
// interrupt triggered function to eat data from send queues and transmit it
void checkSendQueues() {
MessageBuffer_t SendBuffer;
#ifdef HAS_LORA
// Check if there is a pending TX/RX job running
if ((LMIC.opmode & (OP_JOINING | OP_REJOIN | OP_TXDATA | OP_POLL)) != 0) {
// LoRa Busy -> don't eat data from queue, since it cannot be sent
} else {
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
// SendBuffer gets struct MessageBuffer with next payload from queue
LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
SendBuffer.MessageSize, (cfg.countermode & 0x02));
ESP_LOGI(TAG, "%d bytes sent to LoRa", SendBuffer.MessageSize);
sprintf(display_line7, "PACKET QUEUED");
}
}
#endif
#ifdef HAS_SPI
if (xQueueReceive(SPISendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize);
}
#endif
} // checkSendQueues
void flushQueues() { void flushQueues() {
#ifdef HAS_LORA #ifdef HAS_LORA
xQueueReset(LoraSendQueue); xQueueReset(LoraSendQueue);

View File

@ -3,7 +3,6 @@
void SendData(uint8_t port); void SendData(uint8_t port);
void sendPayload(void); void sendPayload(void);
void IRAM_ATTR SendCycleIRQ(void);
void checkSendQueues(void); void checkSendQueues(void);
void flushQueues(); void flushQueues();

30
src/spi.cpp Normal file
View File

@ -0,0 +1,30 @@
#ifdef HAS_SPI
#include "globals.h"
// Local logging tag
static const char TAG[] = "main";
MessageBuffer_t SendBuffer;
QueueHandle_t SPISendQueue;
TaskHandle_t SpiTask;
// SPI feed Task
void spi_loop(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
while (1) {
if (xQueueReceive(SPISendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize);
}
vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU
} // end of infinite loop
vTaskDelete(NULL); // shoud never be reached
} // spi_loop()
#endif // HAS_SPI

9
src/spi.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef _SPI_H
#define _SPI_H
extern TaskHandle_t SpiTask;
extern QueueHandle_t SPISendQueue;
void spi_loop(void *pvParameters);
#endif

View File

@ -1,35 +0,0 @@
#include "statemachine.h"
// Local logging tag
static const char TAG[] = "main";
void stateMachine(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
while (1) {
#ifdef HAS_BUTTON
if (ButtonPressedIRQ)
readButton();
#endif
#ifdef HAS_DISPLAY
if (DisplayTimerIRQ)
refreshtheDisplay();
#endif
// check housekeeping cycle and if due do the work
if (HomeCycleIRQ)
doHousekeeping();
// check send cycle and if due enqueue payload to send
if (SendCycleTimerIRQ)
sendPayload();
// check send queues and process due payload to send
checkSendQueues();
// give yield to CPU
vTaskDelay(2 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL); // shoud never be reached
}

View File

@ -1,13 +0,0 @@
#ifndef _STATEMACHINE_H
#define _STATEMACHINE_H
#include "globals.h"
#include "led.h"
#include "wifiscan.h"
#include "senddata.h"
#include "cyclic.h"
void stateMachine(void *pvParameters);
void stateMachineInit();
#endif

View File

@ -46,21 +46,12 @@ void wifi_sniffer_init(void) {
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
} }
// IRQ Handler
void IRAM_ATTR ChannelSwitchIRQ() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// unblock wifi channel rotation task
xSemaphoreGiveFromISR(xWifiChannelSwitchSemaphore, &xHigherPriorityTaskWoken);
}
// Wifi channel rotation task // Wifi channel rotation task
void switchWifiChannel(void *parameter) { void switchWifiChannel(void *parameter) {
while (1) { while (1) {
// task is remaining in block state waiting for channel switch timer ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // waiting for channel switch timer
// interrupt event channel =
xSemaphoreTake(xWifiChannelSwitchSemaphore, portMAX_DELAY); (channel % WIFI_CHANNEL_MAX) + 1; // rotate channel 1..WIFI_CHANNEL_MAX
// rotates variable channel 1..WIFI_CHANNEL_MAX
channel = (channel % WIFI_CHANNEL_MAX) + 1;
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
ESP_LOGD(TAG, "Wifi set channel %d", channel); ESP_LOGD(TAG, "Wifi set channel %d", channel);
} }

View File

@ -27,7 +27,6 @@ typedef struct {
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 IRAM_ATTR ChannelSwitchIRQ(void);
void switchWifiChannel(void * parameter); void switchWifiChannel(void * parameter);
#endif #endif