diff --git a/platformio.ini b/platformio.ini index 1b43cf4f..fe7ad5a5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -26,7 +26,7 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 1.5.16 +release_version = 1.6.0 ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose debug_level = 0 diff --git a/src/button.cpp b/src/button.cpp index 37ae91fe..fd96545c 100644 --- a/src/button.cpp +++ b/src/button.cpp @@ -1,23 +1,12 @@ #ifdef HAS_BUTTON #include "globals.h" -#include "senddata.h" +#include "button.h" // Local logging tag static const char TAG[] = "main"; -portMUX_TYPE mutexButton = portMUX_INITIALIZER_UNLOCKED; - -void IRAM_ATTR ButtonIRQ() { - portENTER_CRITICAL(&mutexButton); - ButtonPressedIRQ++; - portEXIT_CRITICAL(&mutexButton); -} - void readButton() { - portENTER_CRITICAL(&mutexButton); - ButtonPressedIRQ = 0; - portEXIT_CRITICAL(&mutexButton); ESP_LOGI(TAG, "Button pressed"); payload.reset(); payload.addButton(0x01); diff --git a/src/button.h b/src/button.h index 1900ac6d..a7555c0a 100644 --- a/src/button.h +++ b/src/button.h @@ -1,7 +1,8 @@ #ifndef _BUTTON_H #define _BUTTON_H -void IRAM_ATTR ButtonIRQ(void); -void readButton(void); +#include "senddata.h" + +void readButton(); #endif \ No newline at end of file diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 1e864ba5..06d45b62 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -2,22 +2,14 @@ /* Interval can be set in paxcounter.conf (HOMECYCLE) */ // Basic config -#include "globals.h" -#include "senddata.h" -#include "ota.h" +#include "cyclic.h" // Local logging tag static const char TAG[] = "main"; -portMUX_TYPE mutexHomeCycle = portMUX_INITIALIZER_UNLOCKED; - // do all housekeeping void doHousekeeping() { - portENTER_CRITICAL(&mutexHomeCycle); - HomeCycleIRQ = 0; - portEXIT_CRITICAL(&mutexHomeCycle); - // update uptime counter uptime(); @@ -26,17 +18,19 @@ void doHousekeeping() { ESP.restart(); // task storage debugging // -#ifdef HAS_LORA - ESP_LOGD(TAG, "Loraloop %d bytes left", - uxTaskGetStackHighWaterMark(LoraTask)); -#endif ESP_LOGD(TAG, "Wifiloop %d bytes left", uxTaskGetStackHighWaterMark(wifiSwitchTask)); - ESP_LOGD(TAG, "Statemachine %d bytes left", - uxTaskGetStackHighWaterMark(stateMachineTask)); + ESP_LOGD(TAG, "IRQhandler %d bytes left", + uxTaskGetStackHighWaterMark(irqHandlerTask)); #ifdef HAS_GPS ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask)); #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 #ifdef HAS_BATTERY_PROBE @@ -70,12 +64,6 @@ void doHousekeeping() { } } // doHousekeeping() -void IRAM_ATTR homeCycleIRQ() { - portENTER_CRITICAL(&mutexHomeCycle); - HomeCycleIRQ++; - portEXIT_CRITICAL(&mutexHomeCycle); -} - // uptime counter 64bit to prevent millis() rollover after 49 days uint64_t uptime() { static uint32_t low32, high32; diff --git a/src/cyclic.h b/src/cyclic.h index a006f137..6b1e8d73 100644 --- a/src/cyclic.h +++ b/src/cyclic.h @@ -1,8 +1,10 @@ #ifndef _CYCLIC_H #define _CYCLIC_H +#include "globals.h" +#include "senddata.h" + void doHousekeeping(void); -void IRAM_ATTR homeCycleIRQ(void); uint64_t uptime(void); void reset_counters(void); int redirect_log(const char *fmt, va_list args); diff --git a/src/display.cpp b/src/display.cpp index 773dbf94..edcf8cdd 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -15,8 +15,6 @@ const char lora_datarate[] = {"100908078CNA121110090807"}; uint8_t volatile DisplayState = 0; -portMUX_TYPE mutexDisplay = portMUX_INITIALIZER_UNLOCKED; - // helper function, prints a hex key on display void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) { const uint8_t *p; @@ -27,8 +25,9 @@ void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) { u8x8.printf("\n"); } -// show startup screen void init_display(const char *Productname, const char *Version) { + + // show startup screen uint8_t buf[32]; u8x8.begin(); u8x8.setFont(u8x8_font_chroma48medium8_r); @@ -93,10 +92,6 @@ void init_display(const char *Productname, const char *Version) { void refreshtheDisplay() { - portENTER_CRITICAL(&mutexDisplay); - DisplayTimerIRQ = 0; - portEXIT_CRITICAL(&mutexDisplay); - // set display on/off according to current device configuration if (DisplayState != cfg.screenon) { DisplayState = cfg.screenon; @@ -107,7 +102,7 @@ void refreshtheDisplay() { if (!DisplayState) return; - uint8_t msgWaiting = 0; + uint8_t msgWaiting; char buff[16]; // 16 chars line buffer // update counter (lines 0-1) @@ -192,10 +187,4 @@ void refreshtheDisplay() { } // refreshDisplay() -void IRAM_ATTR DisplayIRQ() { - portENTER_CRITICAL_ISR(&mutexDisplay); - DisplayTimerIRQ++; - portEXIT_CRITICAL_ISR(&mutexDisplay); -} - #endif // HAS_DISPLAY \ No newline at end of file diff --git a/src/display.h b/src/display.h index 84ff9b7a..492af565 100644 --- a/src/display.h +++ b/src/display.h @@ -9,6 +9,5 @@ extern HAS_DISPLAY u8x8; void init_display(const char *Productname, const char *Version); void refreshtheDisplay(void); void DisplayKey(const uint8_t *key, uint8_t len, bool lsb); -void IRAM_ATTR DisplayIRQ(void); #endif \ No newline at end of file diff --git a/src/globals.h b/src/globals.h index 739512f1..703b46d0 100644 --- a/src/globals.h +++ b/src/globals.h @@ -46,34 +46,28 @@ extern uint16_t volatile macs_total, macs_wifi, macs_ble, batt_voltage; // display values extern std::set macs; // temp storage for MACs extern hw_timer_t *channelSwitch, *sendCycle; -extern volatile uint8_t SendCycleTimerIRQ, HomeCycleIRQ, DisplayTimerIRQ, - ChannelTimerIRQ, ButtonPressedIRQ; extern std::array::iterator it; extern std::array beacons; -extern SemaphoreHandle_t xWifiChannelSwitchSemaphore; -extern TaskHandle_t stateMachineTask, wifiSwitchTask; +extern TaskHandle_t irqHandlerTask, wifiSwitchTask; #ifdef HAS_GPS -extern TaskHandle_t GpsTask; #include "gps.h" #endif -#ifdef HAS_LED +#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) #include "led.h" #endif #include "payload.h" #ifdef HAS_LORA -extern QueueHandle_t LoraSendQueue; -extern TaskHandle_t LoraTask; #include "lorawan.h" #endif #ifdef HAS_SPI -extern QueueHandle_t SPISendQueue; +#include "spi.h" #endif #ifdef HAS_DISPLAY diff --git a/src/gps.cpp b/src/gps.cpp index 849ca30e..d2958a8b 100644 --- a/src/gps.cpp +++ b/src/gps.cpp @@ -7,6 +7,7 @@ static const char TAG[] = "main"; TinyGPSPlus gps; gpsStatus_t gps_status; +TaskHandle_t GpsTask; // read GPS data and cast to global struct void gps_read() { @@ -67,6 +68,8 @@ void gps_loop(void *pvParameters) { } // end of infinite loop + vTaskDelete(NULL); // shoud never be reached + } // gps_loop() #endif // HAS_GPS \ No newline at end of file diff --git a/src/gps.h b/src/gps.h index 5121528e..b5cbff88 100644 --- a/src/gps.h +++ b/src/gps.h @@ -19,6 +19,7 @@ typedef struct { extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe extern gpsStatus_t gps_status; // Make struct for storing gps data globally available +extern TaskHandle_t GpsTask; void gps_read(void); void gps_loop(void *pvParameters); diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp new file mode 100644 index 00000000..61aa6d2e --- /dev/null +++ b/src/irqhandler.cpp @@ -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 diff --git a/src/irqhandler.h b/src/irqhandler.h new file mode 100644 index 00000000..f21e484c --- /dev/null +++ b/src/irqhandler.h @@ -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 diff --git a/src/led.cpp b/src/led.cpp index 0ade583a..59965dce 100644 --- a/src/led.cpp +++ b/src/led.cpp @@ -6,6 +6,8 @@ led_states LEDState = LED_OFF; // LED state global for state machine led_states previousLEDState = 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 unsigned long LEDBlinkStarted = 0; // When (in millis() led blink started) @@ -94,69 +96,75 @@ void blink_LED(uint16_t set_color, uint16_t set_blinkduration) { LEDState = LED_ON; // Let main set LED on } -void led_loop() { - // Custom blink running always have priority other LoRaWAN led management - if (LEDBlinkStarted && LEDBlinkDuration) { - // Custom blink is finished, let this order, avoid millis() overflow - if ((millis() - LEDBlinkStarted) >= LEDBlinkDuration) { - // Led becomes off, and stop blink - LEDState = LED_OFF; - LEDBlinkStarted = 0; - LEDBlinkDuration = 0; - LEDColor = COLOR_NONE; +void ledLoop(void *parameter) { + while (1) { + // Custom blink running always have priority other LoRaWAN led management + if (LEDBlinkStarted && LEDBlinkDuration) { + // Custom blink is finished, let this order, avoid millis() overflow + if ((millis() - LEDBlinkStarted) >= LEDBlinkDuration) { + // Led becomes off, and stop blink + LEDState = LED_OFF; + LEDBlinkStarted = 0; + LEDBlinkDuration = 0; + LEDColor = COLOR_NONE; + } else { + // In case of LoRaWAN led management blinked off + LEDState = LED_ON; + } + // No custom blink, check LoRaWAN state } else { - // In case of LoRaWAN led management blinked off - LEDState = LED_ON; - } - // No custom blink, check LoRaWAN state - } else { #ifdef HAS_LORA - // LED indicators for viusalizing LoRaWAN state - if (LMIC.opmode & (OP_JOINING | OP_REJOIN)) { - LEDColor = COLOR_YELLOW; - // quick blink 20ms on each 1/5 second - LEDState = ((millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending - } else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) { - LEDColor = COLOR_BLUE; - // small blink 10ms on each 1/2sec (not when joining) - LEDState = ((millis() % 500) < 10) ? LED_ON : LED_OFF; - // This should not happen so indicate a problem - } else if (LMIC.opmode & - ((OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0)) { - LEDColor = COLOR_RED; - // heartbeat long blink 200ms on each 2 seconds - LEDState = ((millis() % 2000) < 200) ? LED_ON : LED_OFF; - } else + // LED indicators for viusalizing LoRaWAN state + if (LMIC.opmode & (OP_JOINING | OP_REJOIN)) { + LEDColor = COLOR_YELLOW; + // quick blink 20ms on each 1/5 second + LEDState = + ((millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending + } else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) { + LEDColor = COLOR_BLUE; + // small blink 10ms on each 1/2sec (not when joining) + LEDState = ((millis() % 500) < 10) ? LED_ON : LED_OFF; + // This should not happen so indicate a problem + } else if (LMIC.opmode & + ((OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0)) { + LEDColor = COLOR_RED; + // heartbeat long blink 200ms on each 2 seconds + LEDState = ((millis() % 2000) < 200) ? LED_ON : LED_OFF; + } else #endif // HAS_LORA - { - // led off - LEDColor = COLOR_NONE; - LEDState = LED_OFF; + { + // led off + LEDColor = COLOR_NONE; + LEDState = LED_OFF; + } } - } - // led need to change state? avoid digitalWrite() for nothing - if (LEDState != previousLEDState) { - if (LEDState == LED_ON) { - rgb_set_color(LEDColor); + // led need to change state? avoid digitalWrite() for nothing + if (LEDState != previousLEDState) { + if (LEDState == LED_ON) { + rgb_set_color(LEDColor); #ifdef LED_ACTIVE_LOW - digitalWrite(HAS_LED, LOW); + digitalWrite(HAS_LED, LOW); #else - digitalWrite(HAS_LED, HIGH); + digitalWrite(HAS_LED, HIGH); #endif - } else { - rgb_set_color(COLOR_NONE); + } else { + rgb_set_color(COLOR_NONE); #ifdef LED_ACTIVE_LOW - digitalWrite(HAS_LED, HIGH); + digitalWrite(HAS_LED, HIGH); #else - digitalWrite(HAS_LED, LOW); + digitalWrite(HAS_LED, LOW); #endif + } + 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) diff --git a/src/led.h b/src/led.h index 8dd4716c..2631563c 100644 --- a/src/led.h +++ b/src/led.h @@ -31,9 +31,11 @@ struct RGBColor { enum led_states { LED_OFF, LED_ON }; +extern TaskHandle_t ledLoopTask; + // Exported Functions void rgb_set_color(uint16_t hue); void blink_LED(uint16_t set_color, uint16_t set_blinkduration); -void led_loop(); +void ledLoop(void *parameter); #endif \ No newline at end of file diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 7f93c1e6..d7e85d36 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -6,6 +6,9 @@ // Local logging Tag static const char TAG[] = "lora"; +osjob_t sendjob; +QueueHandle_t LoraSendQueue; + // LMIC enhanced Pin mapping const lmic_pinmap lmic_pins = {.mosi = PIN_SPI_MOSI, .miso = PIN_SPI_MISO, @@ -204,6 +207,9 @@ void onEvent(ev_t ev) { // the library) switch_lora(cfg.lorasf, cfg.txpower); + // kickoff first send job + os_setCallback(&sendjob, lora_send); + // show effective LoRa parameters after join ESP_LOGI(TAG, "ADR=%d, SF=%d, TXPOWER=%d", cfg.adrmode, cfg.lorasf, cfg.txpower); @@ -241,17 +247,6 @@ void onEvent(ev_t ev) { } // 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 void switch_lora(uint8_t sf, uint8_t tx) { 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 \ No newline at end of file diff --git a/src/lorawan.h b/src/lorawan.h index 32220496..66d77f5b 100644 --- a/src/lorawan.h +++ b/src/lorawan.h @@ -14,6 +14,8 @@ #include #endif +extern QueueHandle_t LoraSendQueue; + void onEvent(ev_t ev); void gen_lora_deveui(uint8_t *pdeveui); 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_getDevEui(u1_t *buf); void showLoraKeys(void); -void lorawan_loop(void *pvParameters); void switch_lora(uint8_t sf, uint8_t tx); +void lora_send(osjob_t *job); #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index f758a795..4268bb3a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,19 +27,22 @@ Uused tasks and timers: 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 +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 ========================== - 0 Display-Refresh - 1 Wifi Channel Switch - 2 Send Cycle - 3 Housekeeping + 0 Trigger display refresh + 1 Trigger Wifi channel switch + 2 Trigger send payload cycle + 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, batt_voltage = 0; // globals for display -// hardware timer for cyclic tasks -hw_timer_t *channelSwitch, *displaytimer, *sendCycle, *homeCycle; - -// 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 +hw_timer_t *channelSwitch, *sendCycle, *homeCycle, *displaytimer; // irq tasks +TaskHandle_t irqHandlerTask, wifiSwitchTask; std::set macs; // container holding unique MAC adress hashes @@ -159,12 +140,10 @@ void setup() { strcat_P(features, "PU"); // install button interrupt (pullup mode) pinMode(HAS_BUTTON, INPUT_PULLUP); - attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, RISING); #else strcat_P(features, "PD"); // install button interrupt (pulldown mode) pinMode(HAS_BUTTON, INPUT_PULLDOWN); - attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, FALLING); #endif // BUTTON_PULLUP #endif // HAS_BUTTON @@ -183,6 +162,16 @@ void setup() { } else ESP_LOGI(TAG, "LORA send queue created, size %d Bytes", 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 // initialize SPI @@ -232,16 +221,12 @@ void setup() { // setup display refresh trigger IRQ using esp32 hardware timer // https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ - // prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up displaytimer = timerBegin(0, 80, true); // interrupt handler DisplayIRQ, triggered by edge timerAttachInterrupt(displaytimer, &DisplayIRQ, true); // reload interrupt after each trigger of display refresh cycle timerAlarmWrite(displaytimer, DISPLAYREFRESH_MS * 1000, true); - // enable display interrupt - yield(); - timerAlarmEnable(displaytimer); #endif // setup send cycle trigger IRQ using esp32 hardware timer 2 @@ -255,20 +240,10 @@ void setup() { timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true); // setup channel rotation trigger IRQ using esp32 hardware timer 1 - xWifiChannelSwitchSemaphore = xSemaphoreCreateBinary(); channelSwitch = timerBegin(1, 800, true); timerAttachInterrupt(channelSwitch, &ChannelSwitchIRQ, 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 #if PAYLOAD_ENCODER == 1 strcat_P(features, " PLAIN"); @@ -288,43 +263,6 @@ void setup() { #ifdef VERBOSE showLoraKeys(); #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 // 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 get_salt(); // get new 16bit for salting hashes - // start wifi channel rotation task - 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 */ +#ifdef HAS_GPS + ESP_LOGI(TAG, "Starting GPSloop..."); + 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 + +#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 - ESP_LOGI(TAG, "Starting Statemachine..."); - xTaskCreatePinnedToCore(stateMachine, /* task function */ - "stateloop", /* name of task */ - 2048, /* stack size of task */ - (void *)1, /* parameter of the task */ - 1, /* priority of the task */ - &stateMachineTask, /* task handle */ - 1); /* CPU core */ + ESP_LOGI(TAG, "Starting IRQ Handler..."); + xTaskCreatePinnedToCore(irqHandler, // task function + "irqhandler", // name of task + 2048, // stack size of task + (void *)1, // parameter of the task + 1, // priority of the task + &irqHandlerTask, // task handle + 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() void loop() { -// switch LED state if device has LED(s) -#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) - led_loop(); + while (1) { +#ifdef HAS_LORA + os_runloop_once(); // execute lmic scheduled jobs and events #endif + vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU + } - // give yield to CPU - vTaskDelay(2 / portTICK_PERIOD_MS); + vTaskDelete(NULL); // shoud never be reached } \ No newline at end of file diff --git a/src/main.h b/src/main.h index 40e9c208..9c74aef5 100644 --- a/src/main.h +++ b/src/main.h @@ -6,12 +6,11 @@ #include // needed for timers #include "globals.h" -#include "led.h" #include "wifiscan.h" #include "configmanager.h" #include "cyclic.h" #include "beacon_array.h" #include "ota.h" -#include "statemachine.h" +#include "irqhandler.h" #endif \ No newline at end of file diff --git a/src/senddata.cpp b/src/senddata.cpp index d4f89d62..ee293485 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -1,8 +1,6 @@ // Basic Config #include "globals.h" -portMUX_TYPE mutexSendCycle = portMUX_INITIALIZER_UNLOCKED; - // put data to send in RTos Queues used for transmit over channels Lora and SPI void SendData(uint8_t port) { @@ -39,10 +37,6 @@ void SendData(uint8_t port) { // interrupt triggered function to prepare payload to send void sendPayload() { - portENTER_CRITICAL(&mutexSendCycle); - SendCycleTimerIRQ = 0; - portEXIT_CRITICAL(&mutexSendCycle); - // append counter data to payload payload.reset(); payload.addCount(macs_wifi, cfg.blescan ? macs_ble : 0); @@ -68,40 +62,6 @@ void sendPayload() { SendData(COUNTERPORT); } // 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() { #ifdef HAS_LORA xQueueReset(LoraSendQueue); diff --git a/src/senddata.h b/src/senddata.h index fc236b5f..07819609 100644 --- a/src/senddata.h +++ b/src/senddata.h @@ -3,7 +3,6 @@ void SendData(uint8_t port); void sendPayload(void); -void IRAM_ATTR SendCycleIRQ(void); void checkSendQueues(void); void flushQueues(); diff --git a/src/spi.cpp b/src/spi.cpp new file mode 100644 index 00000000..57da9287 --- /dev/null +++ b/src/spi.cpp @@ -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 \ No newline at end of file diff --git a/src/spi.h b/src/spi.h new file mode 100644 index 00000000..b258a2ce --- /dev/null +++ b/src/spi.h @@ -0,0 +1,9 @@ +#ifndef _SPI_H +#define _SPI_H + +extern TaskHandle_t SpiTask; +extern QueueHandle_t SPISendQueue; + +void spi_loop(void *pvParameters); + +#endif \ No newline at end of file diff --git a/src/statemachine.cpp b/src/statemachine.cpp deleted file mode 100644 index 76d02801..00000000 --- a/src/statemachine.cpp +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/src/statemachine.h b/src/statemachine.h deleted file mode 100644 index ec9966a0..00000000 --- a/src/statemachine.h +++ /dev/null @@ -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 diff --git a/src/wifiscan.cpp b/src/wifiscan.cpp index 4e2500b6..5613a5ec 100644 --- a/src/wifiscan.cpp +++ b/src/wifiscan.cpp @@ -46,21 +46,12 @@ void wifi_sniffer_init(void) { 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 void switchWifiChannel(void *parameter) { while (1) { - // task is remaining in block state waiting for channel switch timer - // interrupt event - xSemaphoreTake(xWifiChannelSwitchSemaphore, portMAX_DELAY); - // rotates variable channel 1..WIFI_CHANNEL_MAX - channel = (channel % WIFI_CHANNEL_MAX) + 1; + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // waiting for channel switch timer + channel = + (channel % WIFI_CHANNEL_MAX) + 1; // rotate channel 1..WIFI_CHANNEL_MAX esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); ESP_LOGD(TAG, "Wifi set channel %d", channel); } diff --git a/src/wifiscan.h b/src/wifiscan.h index 48c218a1..9ad06422 100644 --- a/src/wifiscan.h +++ b/src/wifiscan.h @@ -27,7 +27,6 @@ typedef struct { void wifi_sniffer_init(void); void IRAM_ATTR wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type); -void IRAM_ATTR ChannelSwitchIRQ(void); void switchWifiChannel(void * parameter); #endif \ No newline at end of file