From e5df1013b303be7ec9eb9db987dcaeae8328a6a1 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 3 Oct 2018 00:25:05 +0200 Subject: [PATCH 1/8] v1.5.18 (improved tasking, lmic has now core1 exclusive) --- platformio.ini | 6 +- src/cyclic.cpp | 9 +-- src/cyclic.h | 4 ++ src/display.cpp | 18 +++++- src/globals.h | 6 +- src/gps.h | 1 + src/led.cpp | 104 ++++++++++++++++--------------- src/led.h | 2 +- src/lorawan.cpp | 23 +++---- src/lorawan.h | 4 +- src/main.cpp | 143 ++++++++++++++++++------------------------- src/statemachine.cpp | 5 ++ 12 files changed, 164 insertions(+), 161 deletions(-) diff --git a/platformio.ini b/platformio.ini index 1b43cf4f3..9683cda7e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -26,13 +26,13 @@ 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.5.18 ; 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 ; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA -upload_protocol = esptool -;upload_protocol = custom +;upload_protocol = esptool +upload_protocol = custom extra_scripts = pre:build.py keyfile = ota.conf platform_espressif32 = espressif32@1.4.0 diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 1e864ba5a..4dbff221f 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -3,8 +3,6 @@ // Basic config #include "globals.h" -#include "senddata.h" -#include "ota.h" // Local logging tag static const char TAG[] = "main"; @@ -26,10 +24,6 @@ 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", @@ -37,6 +31,9 @@ void doHousekeeping() { #ifdef HAS_GPS ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask)); #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 diff --git a/src/cyclic.h b/src/cyclic.h index a006f1377..9aad61d44 100644 --- a/src/cyclic.h +++ b/src/cyclic.h @@ -1,6 +1,10 @@ #ifndef _CYCLIC_H #define _CYCLIC_H +#include "senddata.h" +#include "ota.h" +#include "led.h" + void doHousekeeping(void); void IRAM_ATTR homeCycleIRQ(void); uint64_t uptime(void); diff --git a/src/display.cpp b/src/display.cpp index 773dbf946..1eaef6c53 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -15,6 +15,8 @@ const char lora_datarate[] = {"100908078CNA121110090807"}; uint8_t volatile DisplayState = 0; +hw_timer_t *displaytimer; + portMUX_TYPE mutexDisplay = portMUX_INITIALIZER_UNLOCKED; // helper function, prints a hex key on display @@ -27,8 +29,20 @@ 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) { + + // 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 + timerAlarmEnable(displaytimer); + + // show startup screen uint8_t buf[32]; u8x8.begin(); u8x8.setFont(u8x8_font_chroma48medium8_r); @@ -107,7 +121,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) diff --git a/src/globals.h b/src/globals.h index 739512f14..eb0adc110 100644 --- a/src/globals.h +++ b/src/globals.h @@ -56,19 +56,17 @@ extern SemaphoreHandle_t xWifiChannelSwitchSemaphore; extern TaskHandle_t stateMachineTask, 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" +extern TaskHandle_t ledLoopTask; #endif #include "payload.h" #ifdef HAS_LORA -extern QueueHandle_t LoraSendQueue; -extern TaskHandle_t LoraTask; #include "lorawan.h" #endif diff --git a/src/gps.h b/src/gps.h index 5121528ee..b5cbff884 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/led.cpp b/src/led.cpp index 0ade583af..9379d9e40 100644 --- a/src/led.cpp +++ b/src/led.cpp @@ -94,69 +94,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 8dd4716cb..68f5bd888 100644 --- a/src/led.h +++ b/src/led.h @@ -34,6 +34,6 @@ enum led_states { LED_OFF, LED_ON }; // 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 7f93c1e67..49a6b8617 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -63,6 +63,18 @@ void RevBytes(unsigned char *b, size_t c) { } } +// initial lmic job +void initlmic(osjob_t *j) { + // reset 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); + // start joining + LMIC_startJoining(); + // init done - onEvent() callback will be invoked... +} + // LMIC callback functions void os_getDevKey(u1_t *buf) { memcpy(buf, APPKEY, 16); } @@ -241,17 +253,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) diff --git a/src/lorawan.h b/src/lorawan.h index 32220496c..162caa258 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 initlmic(osjob_t *j); #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index f758a7954..0961af1a2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,12 +27,14 @@ 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 read data from GPS over serial or i2c +statemachine 0 1 switches application process logic +IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer task + +looptask 1 1 arduino loop() -> runs the LMIC stack +IDLE 1 0 ESP32 arduino scheduler ESP32 hardware timers ========================== @@ -53,7 +55,7 @@ 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; +hw_timer_t *channelSwitch, *sendCycle, *homeCycle; // this variables will be changed in the ISR, and read in main loop uint8_t volatile ButtonPressedIRQ = 0, ChannelTimerIRQ = 0, @@ -66,7 +68,6 @@ SemaphoreHandle_t xWifiChannelSwitchSemaphore; // RTos send queues for payload transmit #ifdef HAS_LORA QueueHandle_t LoraSendQueue; -TaskHandle_t LoraTask = NULL; #endif #ifdef HAS_SPI @@ -74,7 +75,11 @@ QueueHandle_t SPISendQueue; #endif #ifdef HAS_GPS -TaskHandle_t GpsTask = NULL; +TaskHandle_t GpsTask; +#endif + +#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) +TaskHandle_t ledLoopTask; #endif std::set macs; // container holding unique MAC adress hashes @@ -133,6 +138,7 @@ void setup() { strcat_P(features, " BLE"); #else bool btstop = btStop(); + //esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); #endif // initialize battery status @@ -228,45 +234,25 @@ void setup() { #ifdef HAS_DISPLAY strcat_P(features, " OLED"); DisplayState = cfg.screenon; - init_display(PRODUCTNAME, PROGVERSION); - - // 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 sendCycle = timerBegin(2, 8000, true); timerAttachInterrupt(sendCycle, &SendCycleIRQ, true); timerAlarmWrite(sendCycle, cfg.sendcycle * 2 * 10000, true); + timerAlarmEnable(sendCycle); // setup house keeping cycle trigger IRQ using esp32 hardware timer 3 homeCycle = timerBegin(3, 8000, true); timerAttachInterrupt(homeCycle, &homeCycleIRQ, true); timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true); + timerAlarmEnable(homeCycle); // 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 @@ -288,43 +274,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,14 +292,16 @@ 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 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 state machine ESP_LOGI(TAG, "Starting Statemachine..."); @@ -360,17 +311,41 @@ void setup() { (void *)1, /* parameter of the task */ 1, /* priority of the task */ &stateMachineTask, /* task handle */ - 1); /* CPU core */ - -} // setup() - -void loop() { + 0); /* CPU core */ -// switch LED state if device has LED(s) #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) - led_loop(); + // 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 - // give yield to CPU - vTaskDelay(2 / portTICK_PERIOD_MS); + // 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 */ + +} // setup() + +void loop() { + osjob_t initjob; + // initialize run-time env + os_init(); + // setup initial job + os_setCallback(&initjob, initlmic); + // execute scheduled jobs and events + while (1) { + os_runloop_once(); // execute LMIC jobs + vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU + } } \ No newline at end of file diff --git a/src/statemachine.cpp b/src/statemachine.cpp index 76d02801c..b0476ccb9 100644 --- a/src/statemachine.cpp +++ b/src/statemachine.cpp @@ -7,6 +7,11 @@ void stateMachine(void *pvParameters) { configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check + // initialize display - caution: must be done on core 1 in arduino loop! +#ifdef HAS_DISPLAY + init_display(PRODUCTNAME, PROGVERSION); +#endif + while (1) { #ifdef HAS_BUTTON From 1eceea2686980004411ca3baae109465a2cbc654 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 3 Oct 2018 16:24:45 +0200 Subject: [PATCH 2/8] new lmic tasking --- platformio.ini | 6 ++--- src/cyclic.cpp | 5 +++- src/globals.h | 2 +- src/gps.cpp | 3 +++ src/led.cpp | 2 ++ src/lorawan.cpp | 39 +++++++++++++++++++--------- src/lorawan.h | 2 +- src/main.cpp | 61 +++++++++++++++++++++++--------------------- src/senddata.cpp | 27 -------------------- src/spi.cpp | 30 ++++++++++++++++++++++ src/spi.h | 9 +++++++ src/statemachine.cpp | 7 +++-- 12 files changed, 115 insertions(+), 78 deletions(-) create mode 100644 src/spi.cpp create mode 100644 src/spi.h diff --git a/platformio.ini b/platformio.ini index 9683cda7e..fe7ad5a52 100644 --- a/platformio.ini +++ b/platformio.ini @@ -26,13 +26,13 @@ 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.18 +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 ; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA -;upload_protocol = esptool -upload_protocol = custom +upload_protocol = esptool +;upload_protocol = custom extra_scripts = pre:build.py keyfile = ota.conf platform_espressif32 = espressif32@1.4.0 diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 4dbff221f..39ace8e58 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -26,11 +26,14 @@ void doHousekeeping() { // task storage debugging // ESP_LOGD(TAG, "Wifiloop %d bytes left", uxTaskGetStackHighWaterMark(wifiSwitchTask)); - ESP_LOGD(TAG, "Statemachine %d bytes left", + ESP_LOGD(TAG, "Stateloop %d bytes left", uxTaskGetStackHighWaterMark(stateMachineTask)); #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 diff --git a/src/globals.h b/src/globals.h index eb0adc110..1f863dfe0 100644 --- a/src/globals.h +++ b/src/globals.h @@ -71,7 +71,7 @@ extern TaskHandle_t ledLoopTask; #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 849ca30e6..d2958a8bf 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/led.cpp b/src/led.cpp index 9379d9e40..59965dcee 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) diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 49a6b8617..d7e85d362 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, @@ -63,18 +66,6 @@ void RevBytes(unsigned char *b, size_t c) { } } -// initial lmic job -void initlmic(osjob_t *j) { - // reset 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); - // start joining - LMIC_startJoining(); - // init done - onEvent() callback will be invoked... -} - // LMIC callback functions void os_getDevKey(u1_t *buf) { memcpy(buf, APPKEY, 16); } @@ -216,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); @@ -300,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 162caa258..66d77f5bd 100644 --- a/src/lorawan.h +++ b/src/lorawan.h @@ -25,6 +25,6 @@ void os_getArtEui(u1_t *buf); void os_getDevEui(u1_t *buf); void showLoraKeys(void); void switch_lora(uint8_t sf, uint8_t tx); -void initlmic(osjob_t *j); +void lora_send(osjob_t *job); #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 0961af1a2..4b61abdf6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,11 +29,12 @@ Task Core Prio Purpose ==================================================================================== wifiloop 0 4 rotates wifi channels ledloop 0 3 blinks LEDs -gpsloop 0 2 read data from GPS over serial or i2c +gpsloop 0 2 reads data from GPS over serial or i2c +spiloop 0 2 reads/writes data on spi interface statemachine 0 1 switches application process logic -IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer task +IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer -looptask 1 1 arduino loop() -> runs the LMIC stack +looptask 1 1 arduino core -> runs the LMIC LoRa stack IDLE 1 0 ESP32 arduino scheduler ESP32 hardware timers @@ -65,23 +66,6 @@ TaskHandle_t stateMachineTask, wifiSwitchTask; SemaphoreHandle_t xWifiChannelSwitchSemaphore; -// RTos send queues for payload transmit -#ifdef HAS_LORA -QueueHandle_t LoraSendQueue; -#endif - -#ifdef HAS_SPI -QueueHandle_t SPISendQueue; -#endif - -#ifdef HAS_GPS -TaskHandle_t GpsTask; -#endif - -#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) -TaskHandle_t ledLoopTask; -#endif - std::set macs; // container holding unique MAC adress hashes // initialize payload encoder @@ -138,7 +122,6 @@ void setup() { strcat_P(features, " BLE"); #else bool btstop = btStop(); - //esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); #endif // initialize battery status @@ -189,6 +172,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 @@ -293,7 +286,7 @@ void setup() { get_salt(); // get new 16bit for salting hashes #ifdef HAS_GPS - ESP_LOGI(TAG, "Starting GPS..."); + ESP_LOGI(TAG, "Starting GPSloop..."); xTaskCreatePinnedToCore(gps_loop, /* task function */ "gpsloop", /* name of task */ 1024, /* stack size of task */ @@ -303,6 +296,17 @@ void setup() { 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 */ @@ -338,14 +342,13 @@ void setup() { } // setup() void loop() { - osjob_t initjob; - // initialize run-time env - os_init(); - // setup initial job - os_setCallback(&initjob, initlmic); - // execute scheduled jobs and events + while (1) { - os_runloop_once(); // execute LMIC jobs +#ifdef HAS_LORA + os_runloop_once(); // execute lmic scheduled jobs and events +#endif vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU } + + vTaskDelete(NULL); // shoud never be reached } \ No newline at end of file diff --git a/src/senddata.cpp b/src/senddata.cpp index d4f89d620..fa4eb9469 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -75,33 +75,6 @@ void IRAM_ATTR SendCycleIRQ() { 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/spi.cpp b/src/spi.cpp new file mode 100644 index 000000000..57da92879 --- /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 000000000..b258a2ce8 --- /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 index b0476ccb9..2592aa071 100644 --- a/src/statemachine.cpp +++ b/src/statemachine.cpp @@ -27,12 +27,11 @@ void stateMachine(void *pvParameters) { // check housekeeping cycle and if due do the work if (HomeCycleIRQ) doHousekeeping(); - // check send cycle and if due enqueue payload to send + + // 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); } From 219f2347da3b75b10965bc8ccad31be027888b01 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 3 Oct 2018 20:18:01 +0200 Subject: [PATCH 3/8] code sanitizations --- src/main.cpp | 36 ++++++++++++++++++------------------ src/statemachine.cpp | 4 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 4b61abdf6..29d1b4335 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,10 +39,10 @@ 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 */ @@ -320,24 +320,24 @@ void setup() { #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 */ + 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 */ + 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 } // setup() diff --git a/src/statemachine.cpp b/src/statemachine.cpp index 2592aa071..c1607fb60 100644 --- a/src/statemachine.cpp +++ b/src/statemachine.cpp @@ -28,10 +28,10 @@ void stateMachine(void *pvParameters) { if (HomeCycleIRQ) doHousekeeping(); - // check send cycle and if due enqueue payload to send + // check send cycle and if due enqueue payload to send if (SendCycleTimerIRQ) sendPayload(); - + // give yield to CPU vTaskDelay(2 / portTICK_PERIOD_MS); } From 6e47a60f3227a6184868dcaf8e009caa809d2c14 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 3 Oct 2018 23:07:22 +0200 Subject: [PATCH 4/8] code sanitization --- src/button.cpp | 1 - src/button.h | 2 ++ src/cyclic.cpp | 2 +- src/cyclic.h | 3 +-- src/globals.h | 1 - src/led.h | 2 ++ src/main.h | 1 - src/statemachine.h | 3 --- 8 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/button.cpp b/src/button.cpp index 37ae91fe6..086c3231b 100644 --- a/src/button.cpp +++ b/src/button.cpp @@ -1,7 +1,6 @@ #ifdef HAS_BUTTON #include "globals.h" -#include "senddata.h" // Local logging tag static const char TAG[] = "main"; diff --git a/src/button.h b/src/button.h index 1900ac6da..9cb6e7b49 100644 --- a/src/button.h +++ b/src/button.h @@ -1,6 +1,8 @@ #ifndef _BUTTON_H #define _BUTTON_H +#include "senddata.h" + void IRAM_ATTR ButtonIRQ(void); void readButton(void); diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 39ace8e58..5f7ca3dc2 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -2,7 +2,7 @@ /* Interval can be set in paxcounter.conf (HOMECYCLE) */ // Basic config -#include "globals.h" +#include "cyclic.h" // Local logging tag static const char TAG[] = "main"; diff --git a/src/cyclic.h b/src/cyclic.h index 9aad61d44..d8ee73b2d 100644 --- a/src/cyclic.h +++ b/src/cyclic.h @@ -1,9 +1,8 @@ #ifndef _CYCLIC_H #define _CYCLIC_H +#include "globals.h" #include "senddata.h" -#include "ota.h" -#include "led.h" void doHousekeeping(void); void IRAM_ATTR homeCycleIRQ(void); diff --git a/src/globals.h b/src/globals.h index 1f863dfe0..38d9e4eea 100644 --- a/src/globals.h +++ b/src/globals.h @@ -61,7 +61,6 @@ extern TaskHandle_t stateMachineTask, wifiSwitchTask; #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) #include "led.h" -extern TaskHandle_t ledLoopTask; #endif #include "payload.h" diff --git a/src/led.h b/src/led.h index 68f5bd888..2631563c2 100644 --- a/src/led.h +++ b/src/led.h @@ -31,6 +31,8 @@ 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); diff --git a/src/main.h b/src/main.h index 40e9c2089..ff4ae180e 100644 --- a/src/main.h +++ b/src/main.h @@ -6,7 +6,6 @@ #include // needed for timers #include "globals.h" -#include "led.h" #include "wifiscan.h" #include "configmanager.h" #include "cyclic.h" diff --git a/src/statemachine.h b/src/statemachine.h index ec9966a0f..a2a870972 100644 --- a/src/statemachine.h +++ b/src/statemachine.h @@ -2,9 +2,6 @@ #define _STATEMACHINE_H #include "globals.h" -#include "led.h" -#include "wifiscan.h" -#include "senddata.h" #include "cyclic.h" void stateMachine(void *pvParameters); From 30d22aa896e6551d518808e03ffe6616036e5368 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 4 Oct 2018 22:08:54 +0200 Subject: [PATCH 5/8] irq handling reworked (using tasknotify instead of semaphores) --- src/button.cpp | 12 +------ src/button.h | 4 +-- src/cyclic.cpp | 16 ++------- src/display.cpp | 25 -------------- src/globals.h | 5 +-- src/irqhandler.cpp | 74 ++++++++++++++++++++++++++++++++++++++++ src/irqhandler.h | 18 ++++++++++ src/main.cpp | 81 ++++++++++++++++++++++++-------------------- src/main.h | 2 +- src/senddata.cpp | 13 ------- src/statemachine.cpp | 39 --------------------- src/statemachine.h | 10 ------ src/wifiscan.cpp | 15 ++------ 13 files changed, 146 insertions(+), 168 deletions(-) create mode 100644 src/irqhandler.cpp create mode 100644 src/irqhandler.h delete mode 100644 src/statemachine.cpp delete mode 100644 src/statemachine.h diff --git a/src/button.cpp b/src/button.cpp index 086c3231b..fd96545c7 100644 --- a/src/button.cpp +++ b/src/button.cpp @@ -1,22 +1,12 @@ #ifdef HAS_BUTTON #include "globals.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 9cb6e7b49..bae8e6b50 100644 --- a/src/button.h +++ b/src/button.h @@ -3,7 +3,7 @@ #include "senddata.h" -void IRAM_ATTR ButtonIRQ(void); -void readButton(void); +void IRAM_ATTR ButtonIRQ(); +void readButton(); #endif \ No newline at end of file diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 5f7ca3dc2..06d45b62e 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -7,15 +7,9 @@ // 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,8 +20,8 @@ void doHousekeeping() { // task storage debugging // ESP_LOGD(TAG, "Wifiloop %d bytes left", uxTaskGetStackHighWaterMark(wifiSwitchTask)); - ESP_LOGD(TAG, "Stateloop %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 @@ -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/display.cpp b/src/display.cpp index 1eaef6c53..edcf8cdd5 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -15,10 +15,6 @@ const char lora_datarate[] = {"100908078CNA121110090807"}; uint8_t volatile DisplayState = 0; -hw_timer_t *displaytimer; - -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; @@ -31,17 +27,6 @@ void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) { void init_display(const char *Productname, const char *Version) { - // 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 - timerAlarmEnable(displaytimer); - // show startup screen uint8_t buf[32]; u8x8.begin(); @@ -107,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; @@ -206,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/globals.h b/src/globals.h index 38d9e4eea..703b46d07 100644 --- a/src/globals.h +++ b/src/globals.h @@ -46,14 +46,11 @@ 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 #include "gps.h" diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp new file mode 100644 index 000000000..61aa6d2e3 --- /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 000000000..fab9bee1d --- /dev/null +++ b/src/irqhandler.h @@ -0,0 +1,18 @@ +#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 "button.h" +#include "display.h" +#include "cyclic.h" +#include "senddata.h" + +void irqHandler(void *pvParameters); + +#endif diff --git a/src/main.cpp b/src/main.cpp index 29d1b4335..abc94c9ec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,10 +31,10 @@ 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 -statemachine 0 1 switches application process logic 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 @@ -55,16 +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, *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; +hw_timer_t *channelSwitch, *sendCycle, *homeCycle, *displaytimer; // irq tasks +TaskHandle_t irqHandlerTask, wifiSwitchTask; std::set macs; // container holding unique MAC adress hashes @@ -227,26 +219,32 @@ void setup() { #ifdef HAS_DISPLAY strcat_P(features, " OLED"); DisplayState = cfg.screenon; + init_display(PRODUCTNAME, PROGVERSION); + + // 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); #endif // setup send cycle trigger IRQ using esp32 hardware timer 2 sendCycle = timerBegin(2, 8000, true); timerAttachInterrupt(sendCycle, &SendCycleIRQ, true); timerAlarmWrite(sendCycle, cfg.sendcycle * 2 * 10000, true); - timerAlarmEnable(sendCycle); // setup house keeping cycle trigger IRQ using esp32 hardware timer 3 homeCycle = timerBegin(3, 8000, true); timerAttachInterrupt(homeCycle, &homeCycleIRQ, true); timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true); - timerAlarmEnable(homeCycle); // 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); - timerAlarmEnable(channelSwitch); // show payload encoder #if PAYLOAD_ENCODER == 1 @@ -287,35 +285,35 @@ void setup() { #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 */ + 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 */ + 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 */ - 0); /* 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 @@ -339,6 +337,15 @@ void setup() { &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); + } // setup() void loop() { diff --git a/src/main.h b/src/main.h index ff4ae180e..9c74aef5d 100644 --- a/src/main.h +++ b/src/main.h @@ -11,6 +11,6 @@ #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 fa4eb9469..ee2934851 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,13 +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); -} - void flushQueues() { #ifdef HAS_LORA xQueueReset(LoraSendQueue); diff --git a/src/statemachine.cpp b/src/statemachine.cpp deleted file mode 100644 index c1607fb60..000000000 --- a/src/statemachine.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "statemachine.h" - -// Local logging tag -static const char TAG[] = "main"; - -void stateMachine(void *pvParameters) { - - configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check - - // initialize display - caution: must be done on core 1 in arduino loop! -#ifdef HAS_DISPLAY - init_display(PRODUCTNAME, PROGVERSION); -#endif - - 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(); - - // 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 a2a870972..000000000 --- a/src/statemachine.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _STATEMACHINE_H -#define _STATEMACHINE_H - -#include "globals.h" -#include "cyclic.h" - -void stateMachine(void *pvParameters); -void stateMachineInit(); - -#endif diff --git a/src/wifiscan.cpp b/src/wifiscan.cpp index 4e2500b6e..5613a5ec7 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); } From ae92bf377d0ee12293e764eaef5b03c86e83178a Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 4 Oct 2018 22:59:02 +0200 Subject: [PATCH 6/8] code sanitization --- src/button.h | 1 - src/cyclic.h | 1 - src/display.h | 1 - src/irqhandler.h | 17 +++++++++++++---- src/main.cpp | 11 +++++++++-- src/senddata.h | 1 - src/wifiscan.h | 1 - 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/button.h b/src/button.h index bae8e6b50..a7555c0ac 100644 --- a/src/button.h +++ b/src/button.h @@ -3,7 +3,6 @@ #include "senddata.h" -void IRAM_ATTR ButtonIRQ(); void readButton(); #endif \ No newline at end of file diff --git a/src/cyclic.h b/src/cyclic.h index d8ee73b2d..6b1e8d738 100644 --- a/src/cyclic.h +++ b/src/cyclic.h @@ -5,7 +5,6 @@ #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.h b/src/display.h index 84ff9b7af..492af5651 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/irqhandler.h b/src/irqhandler.h index fab9bee1d..674d825b0 100644 --- a/src/irqhandler.h +++ b/src/irqhandler.h @@ -1,10 +1,10 @@ #ifndef _IRQHANDLER_H #define _IRQHANDLER_H -#define DISPLAY_IRQ 0x01 -#define BUTTON_IRQ 0x02 -#define SENDPAYLOAD_IRQ 0x04 -#define CYCLIC_IRQ 0x08 +#define DISPLAY_IRQ 0x01 +#define BUTTON_IRQ 0x02 +#define SENDPAYLOAD_IRQ 0x04 +#define CYCLIC_IRQ 0x08 #include "globals.h" #include "cyclic.h" @@ -14,5 +14,14 @@ #include "senddata.h" void irqHandler(void *pvParameters); +void IRAM_ATTR ChannelSwitchIRQ(); +void IRAM_ATTR homeCycleIRQ(); +void IRAM_ATTR SendCycleIRQ(); +#ifdef HAS_DISPLAY +void IRAM_ATTR DisplayIRQ(); +#endif +#ifdef HAS_BUTTON +void IRAM_ATTR ButtonIRQ(); +#endif #endif diff --git a/src/main.cpp b/src/main.cpp index abc94c9ec..4268bb3a7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -140,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 @@ -346,6 +344,15 @@ void setup() { 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() { diff --git a/src/senddata.h b/src/senddata.h index fc236b5fd..078196092 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/wifiscan.h b/src/wifiscan.h index 48c218a10..9ad06422c 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 From a5797ad8c4ef5ab09107f38b8be936fd2750e865 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 4 Oct 2018 23:00:47 +0200 Subject: [PATCH 7/8] code sanitization --- src/irqhandler.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/irqhandler.h b/src/irqhandler.h index 674d825b0..2950de841 100644 --- a/src/irqhandler.h +++ b/src/irqhandler.h @@ -10,7 +10,6 @@ #include "cyclic.h" #include "button.h" #include "display.h" -#include "cyclic.h" #include "senddata.h" void irqHandler(void *pvParameters); From d587a8b8742cc4a282724525edfd79031fee8180 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 4 Oct 2018 23:24:34 +0200 Subject: [PATCH 8/8] code sanitization --- src/irqhandler.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/irqhandler.h b/src/irqhandler.h index 2950de841..f21e484c2 100644 --- a/src/irqhandler.h +++ b/src/irqhandler.h @@ -8,18 +8,20 @@ #include "globals.h" #include "cyclic.h" -#include "button.h" -#include "display.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