v1.5.18 (improved tasking, lmic has now core1 exclusive)

This commit is contained in:
Klaus K Wilting 2018-10-03 00:25:05 +02:00
parent f9ab110289
commit e5df1013b3
12 changed files with 164 additions and 161 deletions

View File

@ -26,13 +26,13 @@ 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.5.18
; 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
; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA ; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA
upload_protocol = esptool ;upload_protocol = esptool
;upload_protocol = custom upload_protocol = custom
extra_scripts = pre:build.py extra_scripts = pre:build.py
keyfile = ota.conf keyfile = ota.conf
platform_espressif32 = espressif32@1.4.0 platform_espressif32 = espressif32@1.4.0

View File

@ -3,8 +3,6 @@
// Basic config // Basic config
#include "globals.h" #include "globals.h"
#include "senddata.h"
#include "ota.h"
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
@ -26,10 +24,6 @@ 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, "Statemachine %d bytes left",
@ -37,6 +31,9 @@ void doHousekeeping() {
#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
#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

View File

@ -1,6 +1,10 @@
#ifndef _CYCLIC_H #ifndef _CYCLIC_H
#define _CYCLIC_H #define _CYCLIC_H
#include "senddata.h"
#include "ota.h"
#include "led.h"
void doHousekeeping(void); void doHousekeeping(void);
void IRAM_ATTR homeCycleIRQ(void); void IRAM_ATTR homeCycleIRQ(void);
uint64_t uptime(void); uint64_t uptime(void);

View File

@ -15,6 +15,8 @@ const char lora_datarate[] = {"100908078CNA121110090807"};
uint8_t volatile DisplayState = 0; uint8_t volatile DisplayState = 0;
hw_timer_t *displaytimer;
portMUX_TYPE mutexDisplay = portMUX_INITIALIZER_UNLOCKED; portMUX_TYPE mutexDisplay = portMUX_INITIALIZER_UNLOCKED;
// helper function, prints a hex key on display // 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"); u8x8.printf("\n");
} }
// show startup screen
void init_display(const char *Productname, const char *Version) { 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]; uint8_t buf[32];
u8x8.begin(); u8x8.begin();
u8x8.setFont(u8x8_font_chroma48medium8_r); u8x8.setFont(u8x8_font_chroma48medium8_r);
@ -107,7 +121,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)

View File

@ -56,19 +56,17 @@ extern SemaphoreHandle_t xWifiChannelSwitchSemaphore;
extern TaskHandle_t stateMachineTask, 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"
extern TaskHandle_t ledLoopTask;
#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

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);

View File

@ -94,69 +94,75 @@ 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) {
// Custom blink running always have priority other LoRaWAN led management while (1) {
if (LEDBlinkStarted && LEDBlinkDuration) { // Custom blink running always have priority other LoRaWAN led management
// Custom blink is finished, let this order, avoid millis() overflow if (LEDBlinkStarted && LEDBlinkDuration) {
if ((millis() - LEDBlinkStarted) >= LEDBlinkDuration) { // Custom blink is finished, let this order, avoid millis() overflow
// Led becomes off, and stop blink if ((millis() - LEDBlinkStarted) >= LEDBlinkDuration) {
LEDState = LED_OFF; // Led becomes off, and stop blink
LEDBlinkStarted = 0; LEDState = LED_OFF;
LEDBlinkDuration = 0; LEDBlinkStarted = 0;
LEDColor = COLOR_NONE; LEDBlinkDuration = 0;
LEDColor = COLOR_NONE;
} else {
// In case of LoRaWAN led management blinked off
LEDState = LED_ON;
}
// No custom blink, check LoRaWAN state
} else { } else {
// In case of LoRaWAN led management blinked off
LEDState = LED_ON;
}
// No custom blink, check LoRaWAN state
} else {
#ifdef HAS_LORA #ifdef HAS_LORA
// LED indicators for viusalizing LoRaWAN state // LED indicators for viusalizing LoRaWAN state
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 =
} else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) { ((millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending
LEDColor = COLOR_BLUE; } else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) {
// small blink 10ms on each 1/2sec (not when joining) LEDColor = COLOR_BLUE;
LEDState = ((millis() % 500) < 10) ? LED_ON : LED_OFF; // small blink 10ms on each 1/2sec (not when joining)
// This should not happen so indicate a problem LEDState = ((millis() % 500) < 10) ? LED_ON : LED_OFF;
} else if (LMIC.opmode & // This should not happen so indicate a problem
((OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0)) { } else if (LMIC.opmode &
LEDColor = COLOR_RED; ((OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0)) {
// heartbeat long blink 200ms on each 2 seconds LEDColor = COLOR_RED;
LEDState = ((millis() % 2000) < 200) ? LED_ON : LED_OFF; // heartbeat long blink 200ms on each 2 seconds
} else LEDState = ((millis() % 2000) < 200) ? LED_ON : LED_OFF;
} else
#endif // HAS_LORA #endif // HAS_LORA
{ {
// led off // led off
LEDColor = COLOR_NONE; LEDColor = COLOR_NONE;
LEDState = LED_OFF; LEDState = LED_OFF;
}
} }
} // led need to change state? avoid digitalWrite() for nothing
// led need to change state? avoid digitalWrite() for nothing if (LEDState != previousLEDState) {
if (LEDState != previousLEDState) { if (LEDState == LED_ON) {
if (LEDState == LED_ON) { rgb_set_color(LEDColor);
rgb_set_color(LEDColor);
#ifdef LED_ACTIVE_LOW #ifdef LED_ACTIVE_LOW
digitalWrite(HAS_LED, LOW); digitalWrite(HAS_LED, LOW);
#else #else
digitalWrite(HAS_LED, HIGH); digitalWrite(HAS_LED, HIGH);
#endif #endif
} else { } else {
rgb_set_color(COLOR_NONE); rgb_set_color(COLOR_NONE);
#ifdef LED_ACTIVE_LOW #ifdef LED_ACTIVE_LOW
digitalWrite(HAS_LED, HIGH); digitalWrite(HAS_LED, HIGH);
#else #else
digitalWrite(HAS_LED, LOW); digitalWrite(HAS_LED, LOW);
#endif #endif
}
previousLEDState = LEDState;
} }
previousLEDState = LEDState; // give yield to CPU
} vTaskDelay(2 / portTICK_PERIOD_MS);
}; // led_loop() } // 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

@ -34,6 +34,6 @@ enum led_states { LED_OFF, LED_ON };
// 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

@ -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 // LMIC callback functions
void os_getDevKey(u1_t *buf) { memcpy(buf, APPKEY, 16); } void os_getDevKey(u1_t *buf) { memcpy(buf, APPKEY, 16); }
@ -241,17 +253,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)

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 initlmic(osjob_t *j);
#endif #endif

View File

@ -27,12 +27,14 @@ 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 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 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 batt_voltage = 0; // globals for display
// hardware timer for cyclic tasks // 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 // this variables will be changed in the ISR, and read in main loop
uint8_t volatile ButtonPressedIRQ = 0, ChannelTimerIRQ = 0, uint8_t volatile ButtonPressedIRQ = 0, ChannelTimerIRQ = 0,
@ -66,7 +68,6 @@ SemaphoreHandle_t xWifiChannelSwitchSemaphore;
// RTos send queues for payload transmit // RTos send queues for payload transmit
#ifdef HAS_LORA #ifdef HAS_LORA
QueueHandle_t LoraSendQueue; QueueHandle_t LoraSendQueue;
TaskHandle_t LoraTask = NULL;
#endif #endif
#ifdef HAS_SPI #ifdef HAS_SPI
@ -74,7 +75,11 @@ QueueHandle_t SPISendQueue;
#endif #endif
#ifdef HAS_GPS #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 #endif
std::set<uint16_t> macs; // container holding unique MAC adress hashes std::set<uint16_t> macs; // container holding unique MAC adress hashes
@ -133,6 +138,7 @@ void setup() {
strcat_P(features, " BLE"); strcat_P(features, " BLE");
#else #else
bool btstop = btStop(); bool btstop = btStop();
//esp_bt_controller_mem_release(ESP_BT_MODE_BTDM);
#endif #endif
// initialize battery status // initialize battery status
@ -228,45 +234,25 @@ void setup() {
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
strcat_P(features, " OLED"); strcat_P(features, " OLED");
DisplayState = cfg.screenon; 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 #endif
// setup send cycle trigger IRQ using esp32 hardware timer 2 // setup send cycle trigger IRQ using esp32 hardware timer 2
sendCycle = timerBegin(2, 8000, true); sendCycle = timerBegin(2, 8000, true);
timerAttachInterrupt(sendCycle, &SendCycleIRQ, true); timerAttachInterrupt(sendCycle, &SendCycleIRQ, true);
timerAlarmWrite(sendCycle, cfg.sendcycle * 2 * 10000, true); timerAlarmWrite(sendCycle, cfg.sendcycle * 2 * 10000, true);
timerAlarmEnable(sendCycle);
// setup house keeping cycle trigger IRQ using esp32 hardware timer 3 // setup house keeping cycle trigger IRQ using esp32 hardware timer 3
homeCycle = timerBegin(3, 8000, true); homeCycle = timerBegin(3, 8000, true);
timerAttachInterrupt(homeCycle, &homeCycleIRQ, true); timerAttachInterrupt(homeCycle, &homeCycleIRQ, true);
timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true); timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true);
timerAlarmEnable(homeCycle);
// setup channel rotation trigger IRQ using esp32 hardware timer 1 // setup channel rotation trigger IRQ using esp32 hardware timer 1
xWifiChannelSwitchSemaphore = xSemaphoreCreateBinary(); 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); timerAlarmEnable(channelSwitch);
// show payload encoder // show payload encoder
@ -288,43 +274,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,14 +292,16 @@ 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 GPS...");
"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
// start state machine // start state machine
ESP_LOGI(TAG, "Starting Statemachine..."); ESP_LOGI(TAG, "Starting Statemachine...");
@ -360,17 +311,41 @@ void setup() {
(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 */ &stateMachineTask, /* task handle */
1); /* CPU core */ 0); /* 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 */
} // setup() } // setup()
void loop() { void loop() {
osjob_t initjob;
// switch LED state if device has LED(s) // initialize run-time env
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) os_init();
led_loop(); // setup initial job
#endif os_setCallback(&initjob, initlmic);
// execute scheduled jobs and events
// give yield to CPU while (1) {
vTaskDelay(2 / portTICK_PERIOD_MS); os_runloop_once(); // execute LMIC jobs
vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU
}
} }

View File

@ -7,6 +7,11 @@ void stateMachine(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check 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) { while (1) {
#ifdef HAS_BUTTON #ifdef HAS_BUTTON