ESP32-PaxCounter/src/main.cpp

512 lines
15 KiB
C++
Raw Normal View History

2018-06-10 22:46:13 +02:00
/*
//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\
2018-06-10 22:46:13 +02:00
2020-09-26 21:22:55 +02:00
Copyright 2018-2020 Oliver Brandmueller <ob@sysadm.in>
Copyright 2018-2020 Klaus Wilting <verkehrsrot@arcor.de>
2018-06-10 22:46:13 +02:00
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
2019-04-07 21:54:19 +02:00
NOTE:
2018-06-10 22:46:13 +02:00
Parts of the source files in this repository are made available under different
licenses. Refer to LICENSE.txt file in repository for more details.
2018-09-22 21:26:11 +02:00
//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\
2019-03-03 12:57:00 +01:00
// Tasks and timers:
2018-09-22 21:26:11 +02:00
2022-01-26 16:21:44 +01:00
Task Core Prio Purpose
2019-03-03 12:57:00 +01:00
-------------------------------------------------------------------------------
2022-01-26 16:32:54 +01:00
ledloop* 0 1 blinks LEDs
spiloop# 0 2 reads/writes data on spi interface
lmictask* 1 8 MCCI LMiC LORAWAN stack
clockloop# 1 6 generates realtime telegrams for external clock
mqttloop# 1 5 reads/writes data on ETH interface
timesync_proc# 1 7 processes realtime time sync requests
irqhandler# 1 4 cyclic tasks (i.e. displayrefresh) triggered by
gpsloop* 1 3 reads data from GPS via serial or i2c
lorasendtask# 1 2 feeds data from lora sendqueue to lmcic
rmcd_process# 1 1 Remote command interpreter loop
2022-01-26 16:21:44 +01:00
* spinning task
# blocked/waiting task
2018-09-22 21:26:11 +02:00
2018-12-22 21:40:43 +01:00
Low priority numbers denote low priority tasks.
2022-01-26 16:21:44 +01:00
-------------------------------------------------------------------------------
2019-04-07 21:54:19 +02:00
2019-03-03 12:57:00 +01:00
// ESP32 hardware timers
-------------------------------------------------------------------------------
2019-03-19 00:02:35 +01:00
0 displayIRQ -> display refresh -> 40ms (DISPLAYREFRESH_MS)
1 ppsIRQ -> pps clock irq -> 1sec
2022-02-07 15:38:15 +01:00
2 (unused)
2019-05-07 21:17:06 +02:00
3 MatrixDisplayIRQ -> matrix mux cycle -> 0,5ms (MATRIX_DISPLAY_SCAN_US)
2019-03-03 12:57:00 +01:00
2022-02-07 15:38:15 +01:00
// External RTC timer (if present)
2020-02-03 15:31:49 +01:00
-------------------------------------------------------------------------------
2022-02-07 15:38:15 +01:00
triggers pps 1 sec impulse
2020-02-03 15:31:49 +01:00
2022-02-07 15:38:15 +01:00
// Interrupt routines
2020-02-03 15:31:49 +01:00
-------------------------------------------------------------------------------
2022-02-07 15:38:15 +01:00
ISRs fired by CPU or GPIO:
DisplayIRQ <- esp32 timer 0
CLOCKIRQ <- esp32 timer 1 or GPIO (RTC_INT or GPS_INT)
MatrixDisplayIRQ<- esp32 timer 3
ButtonIRQ <- GPIO <- Button
PMUIRQ <- GPIO <- PMU chip
Application IRQs fired by software:
TIMESYNC_IRQ <- setTimeSyncIRQ() <- Ticker.h
CYCLIC_IRQ <- setCyclicIRQ() <- Ticker.h
SENDCYCLE_IRQ <- setSendIRQ() <- xTimer
BME_IRQ <- setBMEIRQ() <- Ticker.h
2020-02-03 15:31:49 +01:00
*/
// Basic Config
#include "main.h"
// local Tag for logging
static const char TAG[] = __FILE__;
2020-02-03 15:31:49 +01:00
char clientId[20] = {0}; // unique ClientID
2020-02-03 15:31:49 +01:00
void setup() {
char features[100] = "";
// create some semaphores for syncing / mutexing tasks
I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus
2020-10-30 12:24:16 +01:00
_ASSERT(I2Caccess != NULL);
2020-02-03 15:31:49 +01:00
I2C_MUTEX_UNLOCK();
// disable brownout detection
#ifdef DISABLE_BROWNOUT
// register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4
(*((uint32_t volatile *)ETS_UNCACHED_ADDR((DR_REG_RTCCNTL_BASE + 0xd4)))) = 0;
#endif
// setup debug output or silence device
#if (VERBOSE)
Serial.begin(115200);
esp_log_level_set("*", ESP_LOG_VERBOSE);
#else
// mute logs completely by redirecting them to silence function
esp_log_level_set("*", ESP_LOG_NONE);
#endif
// load device configuration from NVRAM and set runmode
do_after_reset();
2020-02-03 15:31:49 +01:00
2021-03-25 14:03:13 +01:00
// hash 6 byte device MAC to 4 byte clientID
uint8_t mac[6];
2021-12-22 16:49:03 +01:00
esp_read_mac(mac, ESP_MAC_WIFI_STA);
2021-03-25 14:03:13 +01:00
const uint32_t hashedmac = myhash((const char *)mac, 6);
snprintf(clientId, 20, "paxcounter_%08x", hashedmac);
ESP_LOGI(TAG, "Starting %s v%s (runmode=%d / restarts=%d)", clientId,
PROGVERSION, RTC_runmode, RTC_restarts);
2022-01-26 16:21:44 +01:00
ESP_LOGI(TAG, "code build date: %d", compileTime());
2021-03-25 14:03:13 +01:00
2020-02-03 15:31:49 +01:00
// print chip information on startup if in verbose mode after coldstart
#if (VERBOSE)
if (RTC_runmode == RUNMODE_POWERCYCLE) {
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
ESP_LOGI(TAG,
"This is ESP32 chip with %d CPU cores, WiFi%s%s, silicon revision "
"%d, %dMB %s Flash",
chip_info.cores,
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "",
chip_info.revision, spi_flash_get_chip_size() / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded"
: "external");
ESP_LOGI(TAG, "Internal Total heap %d, internal Free Heap %d",
ESP.getHeapSize(), ESP.getFreeHeap());
#ifdef BOARD_HAS_PSRAM
ESP_LOGI(TAG, "SPIRam Total heap %d, SPIRam Free Heap %d",
ESP.getPsramSize(), ESP.getFreePsram());
#endif
ESP_LOGI(TAG, "ChipRevision %d, Cpu Freq %d, SDK Version %s",
ESP.getChipRevision(), ESP.getCpuFreqMHz(), ESP.getSdkVersion());
ESP_LOGI(TAG, "Flash Size %d, Flash Speed %d", ESP.getFlashChipSize(),
ESP.getFlashChipSpeed());
ESP_LOGI(TAG, "Wifi/BT software coexist version %s",
esp_coex_version_get());
#if (HAS_LORA)
ESP_LOGI(TAG, "IBM LMIC version %d.%d.%d", LMIC_VERSION_MAJOR,
LMIC_VERSION_MINOR, LMIC_VERSION_BUILD);
ESP_LOGI(TAG, "Arduino LMIC version %d.%d.%d.%d",
ARDUINO_LMIC_VERSION_GET_MAJOR(ARDUINO_LMIC_VERSION),
ARDUINO_LMIC_VERSION_GET_MINOR(ARDUINO_LMIC_VERSION),
ARDUINO_LMIC_VERSION_GET_PATCH(ARDUINO_LMIC_VERSION),
ARDUINO_LMIC_VERSION_GET_LOCAL(ARDUINO_LMIC_VERSION));
showLoraKeys();
#endif // HAS_LORA
#if (HAS_GPS)
ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion());
#endif
}
#endif // VERBOSE
// open i2c bus
i2c_init();
// setup power on boards with power management logic
#ifdef EXT_POWER_SW
pinMode(EXT_POWER_SW, OUTPUT);
digitalWrite(EXT_POWER_SW, EXT_POWER_ON);
strcat_P(features, " VEXT");
#endif
2020-05-09 22:48:29 +02:00
#if defined HAS_PMU || defined HAS_IP5306
2020-02-03 15:31:49 +01:00
#ifdef HAS_PMU
AXP192_init();
2020-05-09 22:48:29 +02:00
#elif defined HAS_IP5306
IP5306_init();
#endif
2020-02-03 15:31:49 +01:00
strcat_P(features, " PMU");
#endif
2020-05-09 13:41:19 +02:00
// now that we are powered, we scan i2c bus for devices
2020-12-16 18:54:38 +01:00
if (RTC_runmode == RUNMODE_POWERCYCLE)
i2c_scan();
2020-05-09 13:41:19 +02:00
2020-02-03 15:31:49 +01:00
// initialize display
#ifdef HAS_DISPLAY
strcat_P(features, " OLED");
DisplayIsOn = cfg.screenon;
// display verbose info only after a coldstart (note: blocking call!)
dp_init(RTC_runmode == RUNMODE_POWERCYCLE ? true : false);
2020-02-03 15:31:49 +01:00
#endif
#ifdef BOARD_HAS_PSRAM
2020-10-30 12:24:16 +01:00
_ASSERT(psramFound());
2020-02-03 15:31:49 +01:00
ESP_LOGI(TAG, "PSRAM found and initialized");
strcat_P(features, " PSRAM");
#endif
#ifdef BAT_MEASURE_EN
pinMode(BAT_MEASURE_EN, OUTPUT);
#endif
// initialize leds
#if (HAS_LED != NOT_A_PIN)
pinMode(HAS_LED, OUTPUT);
strcat_P(features, " LED");
#ifdef LED_POWER_SW
pinMode(LED_POWER_SW, OUTPUT);
digitalWrite(LED_POWER_SW, LED_POWER_ON);
#endif
#ifdef HAS_TWO_LED
pinMode(HAS_TWO_LED, OUTPUT);
strcat_P(features, " LED1");
#endif
// use LED for power display if we have additional RGB LED, else for status
#ifdef HAS_RGB_LED
switch_LED(LED_ON);
strcat_P(features, " RGB");
#endif
#endif // HAS_LED
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
// start led loop
ESP_LOGI(TAG, "Starting LED Controller...");
xTaskCreatePinnedToCore(ledLoop, // task function
"ledloop", // name of task
1024, // stack size of task
(void *)1, // parameter of the task
2022-01-26 16:32:54 +01:00
1, // priority of the task
2020-02-03 15:31:49 +01:00
&ledLoopTask, // task handle
0); // CPU core
#endif
// initialize wifi antenna
#ifdef HAS_ANTENNA_SWITCH
strcat_P(features, " ANT");
antenna_init();
antenna_select(cfg.wifiant);
#endif
// initialize battery status
#if (defined BAT_MEASURE_ADC || defined HAS_PMU || defined HAS_IP5306)
2020-02-03 15:31:49 +01:00
strcat_P(features, " BATT");
calibrate_voltage();
2020-04-11 21:30:09 +02:00
batt_level = read_battlevel();
#ifdef HAS_IP5306
printIP5306Stats();
#endif
2020-02-03 15:31:49 +01:00
#endif
#if (USE_OTA)
strcat_P(features, " OTA");
// reboot to firmware update mode if ota trigger switch is set
if (RTC_runmode == RUNMODE_UPDATE)
start_ota_update();
#endif
2021-03-04 21:22:49 +01:00
#if (BOOTMENU)
// start local webserver after each coldstart
if (RTC_runmode == RUNMODE_POWERCYCLE)
2021-03-04 21:22:49 +01:00
start_boot_menu();
#endif
// start local webserver on rcommand request
2021-03-03 16:20:21 +01:00
if (RTC_runmode == RUNMODE_MAINTENANCE)
2021-03-04 21:22:49 +01:00
start_boot_menu();
2021-03-03 16:20:21 +01:00
#if ((WIFICOUNTER) || (BLECOUNTER))
// use libpax timer to trigger cyclic senddata
2021-03-18 09:46:52 +01:00
ESP_LOGI(TAG, "Starting libpax...");
2021-03-02 18:36:34 +01:00
struct libpax_config_t configuration;
libpax_default_config(&configuration);
2021-03-18 09:46:52 +01:00
2021-03-25 14:24:25 +01:00
// configure WIFI sniffing
configuration.wificounter = cfg.wifiscan;
2021-03-18 09:46:52 +01:00
configuration.wifi_channel_map = WIFI_CHANNEL_ALL;
configuration.wifi_channel_switch_interval = cfg.wifichancycle;
configuration.wifi_rssi_threshold = cfg.rssilimit;
2021-03-25 14:24:25 +01:00
ESP_LOGI(TAG, "WIFISCAN: %s", cfg.wifiscan ? "on" : "off");
2021-03-18 09:46:52 +01:00
2021-03-25 14:24:25 +01:00
// configure BLE sniffing
configuration.blecounter = cfg.blescan;
2021-03-18 09:46:52 +01:00
configuration.blescantime = cfg.blescantime;
2021-07-20 19:14:51 +02:00
configuration.ble_rssi_threshold = cfg.rssilimit;
2021-03-25 14:24:25 +01:00
ESP_LOGI(TAG, "BLESCAN: %s", cfg.blescan ? "on" : "off");
2021-03-18 09:46:52 +01:00
int config_update = libpax_update_config(&configuration);
2021-03-25 10:30:38 +01:00
if (config_update != 0) {
2021-03-18 09:46:52 +01:00
ESP_LOGE(TAG, "Error in libpax configuration.");
2021-03-02 18:36:34 +01:00
} else {
2021-03-18 09:46:52 +01:00
init_libpax();
2021-03-02 18:36:34 +01:00
}
#else
// use stand alone timer to trigger cyclic senddata
initSendDataTimer(cfg.sendcycle * 2);
2021-03-02 18:36:34 +01:00
#endif
2021-01-01 15:25:56 +01:00
2020-02-03 15:31:49 +01:00
#if (BLECOUNTER)
strcat_P(features, " BLE");
#endif
2021-03-25 10:30:38 +01:00
// start rcommand processing task
ESP_LOGI(TAG, "Starting rcommand interpreter...");
rcmd_init();
2021-03-18 09:46:52 +01:00
2020-02-03 15:31:49 +01:00
// initialize gps
#if (HAS_GPS)
strcat_P(features, " GPS");
if (gps_init()) {
ESP_LOGI(TAG, "Starting GPS Feed...");
xTaskCreatePinnedToCore(gps_loop, // task function
"gpsloop", // name of task
8192, // stack size of task
2020-02-03 15:31:49 +01:00
(void *)1, // parameter of the task
2022-01-26 16:32:54 +01:00
3, // priority of the task
2020-02-03 15:31:49 +01:00
&GpsTask, // task handle
1); // CPU core
}
#endif
// initialize sensors
#if (HAS_SENSORS)
2020-09-01 11:56:21 +02:00
#if (HAS_SENSOR_1)
strcat_P(features, " SENS(1)");
2020-02-03 15:31:49 +01:00
sensor_init();
#endif
2020-09-01 11:56:21 +02:00
#if (HAS_SENSOR_2)
strcat_P(features, " SENS(2)");
sensor_init();
#endif
#if (HAS_SENSOR_3)
strcat_P(features, " SENS(3)");
sensor_init();
#endif
#endif
2020-02-03 15:31:49 +01:00
// initialize LoRa
#if (HAS_LORA)
strcat_P(features, " LORA");
2020-12-09 10:15:12 +01:00
_ASSERT(lmic_init() == ESP_OK);
2020-02-03 15:31:49 +01:00
#endif
// initialize SPI
#ifdef HAS_SPI
strcat_P(features, " SPI");
2020-10-30 12:24:16 +01:00
_ASSERT(spi_init() == ESP_OK);
2020-02-03 15:31:49 +01:00
#endif
2020-05-16 23:49:34 +02:00
// initialize MQTT
#ifdef HAS_MQTT
strcat_P(features, " MQTT");
2020-10-30 12:24:16 +01:00
_ASSERT(mqtt_init() == ESP_OK);
2020-05-16 23:49:34 +02:00
#endif
2020-09-01 11:56:21 +02:00
#if (HAS_SDCARD)
2020-02-03 15:31:49 +01:00
if (sdcard_init())
strcat_P(features, " SD");
#endif
#if (HAS_SDS011)
2020-04-11 21:30:09 +02:00
ESP_LOGI(TAG, "init fine-dust-sensor");
if (sds011_init())
strcat_P(features, " SDS");
2020-02-03 15:31:49 +01:00
#endif
// initialize matrix display
#ifdef HAS_MATRIX_DISPLAY
strcat_P(features, " LED_MATRIX");
MatrixDisplayIsOn = cfg.screenon;
2019-05-30 13:00:24 +02:00
init_matrix_display(); // note: blocking call
#endif
2018-06-17 11:40:52 +02:00
// show payload encoder
#if PAYLOAD_ENCODER == 1
2018-08-02 11:33:02 +02:00
strcat_P(features, " PLAIN");
2018-06-17 11:40:52 +02:00
#elif PAYLOAD_ENCODER == 2
strcat_P(features, " PACKED");
2018-06-17 11:40:52 +02:00
#elif PAYLOAD_ENCODER == 3
strcat_P(features, " LPPDYN");
2018-07-22 16:12:46 +02:00
#elif PAYLOAD_ENCODER == 4
strcat_P(features, " LPPPKD");
2018-06-17 11:40:52 +02:00
#endif
// initialize RTC
2019-02-02 10:35:20 +01:00
#ifdef HAS_RTC
strcat_P(features, " RTC");
2020-10-30 12:24:16 +01:00
_ASSERT(rtc_init());
#endif
2019-02-02 10:35:20 +01:00
#if defined HAS_DCF77
strcat_P(features, " DCF77");
#endif
#if defined HAS_IF482
strcat_P(features, " IF482");
#endif
2019-04-13 17:39:08 +02:00
#if (WIFICOUNTER)
strcat_P(features, " WIFI");
#else
2020-12-11 16:34:17 +01:00
// remove wifi driver from RAM, if option wifi not compiled
esp_wifi_deinit();
2019-04-13 17:39:08 +02:00
#endif
2018-09-21 18:23:34 +02:00
// start state machine
2019-01-28 00:38:31 +01:00
ESP_LOGI(TAG, "Starting Interrupt Handler...");
xTaskCreatePinnedToCore(irqHandler, // task function
"irqhandler", // name of task
2018-12-22 21:40:43 +01:00
4096, // stack size of task
(void *)1, // parameter of the task
2022-01-26 16:32:54 +01:00
4, // priority of the task
&irqHandlerTask, // task handle
1); // CPU core
2018-09-22 13:43:12 +02:00
2019-03-13 21:15:28 +01:00
// initialize BME sensor (BME280/BME680)
#if (HAS_BME)
2019-03-08 18:13:51 +01:00
#ifdef HAS_BME680
2019-03-13 21:15:28 +01:00
strcat_P(features, " BME680");
#elif defined HAS_BME280
2019-03-08 18:13:51 +01:00
strcat_P(features, " BME280");
2019-12-08 12:58:12 +01:00
#elif defined HAS_BMP180
strcat_P(features, " BMP180");
2019-03-13 21:15:28 +01:00
#endif
if (bme_init())
2020-10-30 12:24:16 +01:00
ESP_LOGI(TAG, "BME sensor initialized");
2020-12-28 18:32:47 +01:00
else {
2020-10-30 12:24:16 +01:00
ESP_LOGE(TAG, "BME sensor could not be initialized");
2022-01-22 13:56:40 +01:00
cfg.payloadmask &= (uint8_t)~MEMS_DATA; // switch off transmit of BME data
2020-12-28 18:32:47 +01:00
}
2018-12-26 13:00:32 +01:00
#endif
2019-03-03 13:07:48 +01:00
// starting timers and interrupts
2020-10-30 12:24:16 +01:00
_ASSERT(irqHandlerTask != NULL); // has interrupt handler task started?
ESP_LOGI(TAG, "Starting Timers...");
2019-03-03 13:07:48 +01:00
// display interrupt
#ifdef HAS_DISPLAY
2021-03-06 16:32:10 +01:00
dp_clear();
dp_contrast(DISPLAYCONTRAST);
2019-03-03 13:07:48 +01:00
// https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/
// prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up
displayIRQ = timerBegin(0, 80, true);
timerAttachInterrupt(displayIRQ, &DisplayIRQ, true);
timerAlarmWrite(displayIRQ, DISPLAYREFRESH_MS * 1000, true);
2019-03-03 12:57:00 +01:00
timerAlarmEnable(displayIRQ);
#endif
2019-03-03 13:07:48 +01:00
// LED Matrix display interrupt
#ifdef HAS_MATRIX_DISPLAY
// https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/
// prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 3, count up
matrixDisplayIRQ = timerBegin(3, 80, true);
timerAttachInterrupt(matrixDisplayIRQ, &MatrixDisplayIRQ, true);
timerAlarmWrite(matrixDisplayIRQ, MATRIX_DISPLAY_SCAN_US, true);
timerAlarmEnable(matrixDisplayIRQ);
#endif
// initialize button
2019-05-03 20:24:42 +02:00
#ifdef HAS_BUTTON
strcat_P(features, " BTN_");
#ifdef BUTTON_PULLUP
strcat_P(features, "PU");
#else
strcat_P(features, "PD");
#endif // BUTTON_PULLUP
button_init(HAS_BUTTON);
#endif // HAS_BUTTON
// cyclic function interrupts
cyclicTimer.attach(HOMECYCLE, setCyclicIRQ);
// only if we have a timesource we do timesync
2022-01-26 16:21:44 +01:00
#if ((TIME_SYNC_LORAWAN) || (TIME_SYNC_LORASERVER) || (HAS_GPS) || (HAS_RTC))
2019-04-07 16:13:04 +02:00
#if (defined HAS_IF482 || defined HAS_DCF77)
ESP_LOGI(TAG, "Starting Clock Controller...");
clock_init();
#endif
2020-03-06 19:24:58 +01:00
#if (TIME_SYNC_LORASERVER) || (TIME_SYNC_LORAWAN)
2019-04-13 13:59:30 +02:00
timesync_init(); // create loraserver time sync task
2019-04-08 21:22:54 +02:00
#endif
ESP_LOGI(TAG, "Starting Timekeeper...");
2020-10-30 12:24:16 +01:00
_ASSERT(timepulse_init()); // setup pps timepulse
timepulse_start(); // starts pps and cyclic time sync
2020-11-09 12:35:51 +01:00
strcat_P(features, " TIME");
2019-04-07 16:13:04 +02:00
#endif // timesync
2019-05-03 20:24:42 +02:00
// show compiled features
ESP_LOGI(TAG, "Features:%s", features);
2019-10-16 21:14:34 +02:00
// set runmode to normal
RTC_runmode = RUNMODE_NORMAL;
2019-08-18 17:45:16 +02:00
vTaskDelete(NULL);
2019-08-18 17:45:16 +02:00
} // setup()
2019-04-07 12:27:38 +02:00
2020-12-11 12:39:25 +01:00
void loop() { vTaskDelete(NULL); }