v1.5.18 (improved tasking, lmic has now core1 exclusive)
This commit is contained in:
parent
f9ab110289
commit
e5df1013b3
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
12
src/led.cpp
12
src/led.cpp
@ -94,7 +94,8 @@ void blink_LED(uint16_t set_color, uint16_t set_blinkduration) {
|
||||
LEDState = LED_ON; // Let main set LED on
|
||||
}
|
||||
|
||||
void led_loop() {
|
||||
void ledLoop(void *parameter) {
|
||||
while (1) {
|
||||
// Custom blink running always have priority other LoRaWAN led management
|
||||
if (LEDBlinkStarted && LEDBlinkDuration) {
|
||||
// Custom blink is finished, let this order, avoid millis() overflow
|
||||
@ -116,7 +117,8 @@ void led_loop() {
|
||||
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
|
||||
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)
|
||||
@ -157,6 +159,10 @@ void led_loop() {
|
||||
}
|
||||
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)
|
||||
|
@ -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
|
@ -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)
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include <Wire.h>
|
||||
#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
|
141
src/main.cpp
141
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<uint16_t> 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*/
|
||||
#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 */
|
||||
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()
|
||||
|
||||
void loop() {
|
||||
|
||||
// switch LED state if device has LED(s)
|
||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||
led_loop();
|
||||
#endif
|
||||
|
||||
// give yield to CPU
|
||||
vTaskDelay(2 / portTICK_PERIOD_MS);
|
||||
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
|
||||
}
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user